diff --git a/docs/agents/python.md b/docs/agents/python.md index 4fe9843cf..b8cfaca87 100644 --- a/docs/agents/python.md +++ b/docs/agents/python.md @@ -6,12 +6,10 @@ remains configured for editor feedback. ## Required checks -- **Tool environment:** Run Python checks in this worktree's `.conda-env`: - `conda run --prefix .conda-env make typecheck` and - `conda run --prefix .conda-env make lint`. If the environment is activated - instead, verify that `python`, `mypy`, and `black` all resolve inside the - same environment before running the Make targets. A `python` path inside - `.conda-env` alone does not establish which tools Make will invoke. +- **Tool environment:** Run Python checks from within an active, isolated project + environment (e.g., `conda`, `venv`, or an agent sandbox). Verify that `python`, + `mypy`, and `black` all resolve to this same environment before running the Make + targets. - **Types:** Annotate functions and methods. Avoid `Any` in signatures. Use `make typecheck` for type checking; it runs the project's mypy target. - **Format and lint:** Format modified Python files with `black` from the @@ -27,15 +25,15 @@ remains configured for editor feedback. ## Testing -- **Test environment:** Run pytest and Make test targets through `.conda-env`. - NumCosmo tests write FFTW wisdom under `~/.numcosmo`, and download tests use - `~/.firecrown`; ensure these directories are writable before running those - tests in a sandbox. Treat a denied cache write as a sandbox failure, then +- **Test environment:** Run `pytest` and Make test targets within your active + environment. NumCosmo tests write FFTW wisdom under `~/.numcosmo`, and download + tests use `~/.firecrown`; ensure these directories are writable before running + those tests in a sandbox. Treat a denied cache write as a sandbox failure, then rerun with scoped access before judging the test result. -- **Avoid monkeypatching**: Prefer use of fixtures, parameterized tests, and +- **Avoid monkeypatching:** Prefer use of fixtures, parameterized tests, and dependency injection to the use of monkeypatching. Use monkeypatching only - when other techniques would lead to more complicated code. Any use of - monkeypatching must contain a comment explaining why monkeypatching it the + when other techniques would lead to more complicated code. Any use of + monkeypatching must contain a comment explaining why monkeypatching is the best choice for that test. ## Review guidance diff --git a/pyproject.toml b/pyproject.toml index 4b99311a7..8059d4a4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -148,10 +148,6 @@ explicit_package_bases = true disallow_any_generics = true disallow_untyped_defs = true -[[tool.mypy.overrides]] -module = ["tests", "tests.*"] -disallow_untyped_defs = false - [[tool.mypy.overrides]] module = "firecrown.connector.cobaya.*" disallow_subclassing_any = false diff --git a/tests/_types.py b/tests/_types.py new file mode 100644 index 000000000..6e3b8eafa --- /dev/null +++ b/tests/_types.py @@ -0,0 +1,36 @@ +"""Shared types for test fixtures.""" + +from typing import TypeAlias, TypedDict + +from numcosmo_py import Nc +import numpy as np +import numpy.typing as npt +import sacc + +from firecrown.metadata_types import TracerNames + +RedshiftDistribution: TypeAlias = tuple[ + npt.NDArray[np.float64], npt.NDArray[np.float64] +] +"""Sampled redshift distribution as (z, dndz), without a normalization guarantee.""" + +WindowedAngularPowerSpectrum: TypeAlias = tuple[ + npt.NDArray[np.int64], npt.NDArray[np.float64], sacc.BandpowerWindow +] +"""Window ell grid, measured bandpowers, and their bandpower window.""" + +SaccGalaxyWindowedFixture: TypeAlias = tuple[ + sacc.Sacc, + dict[str, RedshiftDistribution], + dict[tuple[TracerNames, str], WindowedAngularPowerSpectrum], +] +"""SACC data, distributions by tracer name, and spectra by tracer pair/data type.""" + + +class NumCosmoCosmologyFixture(TypedDict): + """Values provided by the NumCosmo cosmology fixtures.""" + + cosmo: Nc.HICosmo + p_ml: Nc.PowspecML + p_mnl: Nc.PowspecMNL + dist: Nc.Distance diff --git a/tests/app/analysis/test_download.py b/tests/app/analysis/test_download.py index 268a5cd38..13abe76be 100644 --- a/tests/app/analysis/test_download.py +++ b/tests/app/analysis/test_download.py @@ -72,7 +72,7 @@ class TestDownloadFromUrl: @patch("urllib.request.urlretrieve") def test_download_from_url_uses_cache_when_available( - self, mock_retrieve, tmp_path: Path + self, mock_retrieve: MagicMock, tmp_path: Path ) -> None: """Test that cached files are used instead of downloading.""" output_file = tmp_path / "test.txt" @@ -99,7 +99,7 @@ def test_download_from_url_uses_cache_when_available( @patch("urllib.request.urlretrieve") def test_download_from_url_downloads_when_not_cached( - self, mock_retrieve, tmp_path: Path + self, mock_retrieve: MagicMock, tmp_path: Path ) -> None: """Test that file is downloaded when not in cache.""" output_file = tmp_path / "test_new.txt" @@ -113,7 +113,7 @@ def test_download_from_url_downloads_when_not_cached( try: # Mock the download to create the cached file - def mock_download(url, path): + def mock_download(url: str, path: Path) -> None: assert url is not None Path(path).write_text("downloaded content", encoding="utf-8") @@ -135,7 +135,7 @@ def mock_download(url, path): @patch("urllib.request.urlretrieve") def test_download_from_url_with_description( - self, mock_retrieve, tmp_path: Path + self, mock_retrieve: MagicMock, tmp_path: Path ) -> None: """Test download with custom description.""" output_file = tmp_path / "test_desc.txt" @@ -149,7 +149,7 @@ def test_download_from_url_with_description( try: # Mock the download - def mock_download(url, path): + def mock_download(url: str, path: Path) -> None: assert url is not None Path(path).write_text("content", encoding="utf-8") @@ -193,7 +193,7 @@ def test_download_from_url_network_error(self, tmp_path: Path) -> None: @patch("urllib.request.urlretrieve") def test_download_from_url_console_parameter( - self, mock_retrieve, tmp_path: Path + self, mock_retrieve: MagicMock, tmp_path: Path ) -> None: """Test that console parameter is required.""" output_file = tmp_path / "test_console.txt" @@ -207,7 +207,7 @@ def test_download_from_url_console_parameter( try: # Mock the download - def mock_download(_, path): + def mock_download(_: str, path: Path) -> None: Path(path).write_text("content", encoding="utf-8") mock_retrieve.side_effect = mock_download diff --git a/tests/app/analysis/test_types.py b/tests/app/analysis/test_types.py index c6ef426b2..6f3a6345e 100644 --- a/tests/app/analysis/test_types.py +++ b/tests/app/analysis/test_types.py @@ -3,6 +3,8 @@ Tests type definitions, enums, and data models for framework configuration. """ +from pathlib import Path + import pyccl import pytest from pydantic import ValidationError @@ -827,7 +829,7 @@ def test_to_ccl_cosmology_all_optional_fields_false(self) -> None: class TestConfigGeneratorMethods: """Tests for ConfigGenerator methods.""" - def test_config_generator_add_sacc(self, tmp_path) -> None: + def test_config_generator_add_sacc(self, tmp_path: Path) -> None: """Test add_sacc method.""" gen = CobayaConfigGenerator( output_path=tmp_path, @@ -841,7 +843,7 @@ def test_config_generator_add_sacc(self, tmp_path) -> None: gen.add_sacc(sacc_path) assert gen.sacc_path == sacc_path - def test_config_generator_add_factory(self, tmp_path) -> None: + def test_config_generator_add_factory(self, tmp_path: Path) -> None: """Test add_factory method.""" gen = CobayaConfigGenerator( output_path=tmp_path, @@ -855,7 +857,7 @@ def test_config_generator_add_factory(self, tmp_path) -> None: gen.add_factory(factory_path) assert gen.factory_source == factory_path - def test_config_generator_add_build_parameters(self, tmp_path) -> None: + def test_config_generator_add_build_parameters(self, tmp_path: Path) -> None: """Test add_build_parameters method.""" gen = CobayaConfigGenerator( output_path=tmp_path, @@ -869,7 +871,7 @@ def test_config_generator_add_build_parameters(self, tmp_path) -> None: gen.add_build_parameters(params) assert gen.build_parameters == params - def test_config_generator_add_models(self, tmp_path) -> None: + def test_config_generator_add_models(self, tmp_path: Path) -> None: """Test add_models method.""" gen = CobayaConfigGenerator( output_path=tmp_path, @@ -908,14 +910,14 @@ def test_get_path_str_with_string(self) -> None: result = get_path_str("my_string_path", use_absolute=False) assert result == "my_string_path" - def test_get_path_str_with_path_absolute(self, tmp_path) -> None: + def test_get_path_str_with_path_absolute(self, tmp_path: Path) -> None: """Test get_path_str with Path object and absolute flag.""" test_path = tmp_path / "subdir" / "file.txt" result = get_path_str(test_path, use_absolute=True) assert result == test_path.absolute().as_posix() - def test_get_path_str_with_path_relative(self, tmp_path) -> None: + def test_get_path_str_with_path_relative(self, tmp_path: Path) -> None: """Test get_path_str with Path object and relative flag.""" test_path = tmp_path / "subdir" / "file.txt" diff --git a/tests/app/examples/test_cluster_counts.py b/tests/app/examples/test_cluster_counts.py index e97c133ac..3e232283d 100644 --- a/tests/app/examples/test_cluster_counts.py +++ b/tests/app/examples/test_cluster_counts.py @@ -6,6 +6,7 @@ import importlib.util import sys from pathlib import Path +from typing import TypedDict, Unpack from unittest.mock import patch import pytest @@ -26,8 +27,25 @@ from firecrown.modeling_tools import ModelingTools -def make_builder(tmp_path: Path, **kwargs) -> ExampleClusterCounts: - """Create a builder without running the generation workflow.""" +class _BuilderKwargs(TypedDict, total=False): + """Optional constructor arguments exercised by these tests.""" + + prefix: str + dataset: ClusterDataset + use_cluster_counts: bool + use_mean_log_mass: bool + cosmology_spec: Path + + +def make_builder( + tmp_path: Path, **kwargs: Unpack[_BuilderKwargs] +) -> ExampleClusterCounts: + """Create a builder without running the generation workflow. + + :param tmp_path: Output directory for the builder. + :param kwargs: Optional builder settings used by the test. + :returns: Builder with generation suppressed. + """ with patch.object(ExampleClusterCounts, "_proceed_generation"): return ExampleClusterCounts( output_path=tmp_path, target_framework=Frameworks.COSMOSIS, **kwargs diff --git a/tests/app/examples/test_cluster_shear.py b/tests/app/examples/test_cluster_shear.py index 0ceb40dac..d8cc80e3e 100644 --- a/tests/app/examples/test_cluster_shear.py +++ b/tests/app/examples/test_cluster_shear.py @@ -4,6 +4,7 @@ """ from pathlib import Path +from typing import TypedDict, Unpack from unittest.mock import patch import numpy as np @@ -22,8 +23,23 @@ from firecrown.modeling_tools import ModelingTools -def make_builder(tmp_path: Path, **kwargs) -> ExampleClusterShear: - """Create a builder without running the generation workflow.""" +class _BuilderKwargs(TypedDict, total=False): + """Optional constructor arguments exercised by these tests.""" + + prefix: str + profile: ClusterProfile + cosmology_spec: Path + + +def make_builder( + tmp_path: Path, **kwargs: Unpack[_BuilderKwargs] +) -> ExampleClusterShear: + """Create a builder without running the generation workflow. + + :param tmp_path: Output directory for the builder. + :param kwargs: Optional builder settings used by the test. + :returns: Builder with generation suppressed. + """ with patch.object(ExampleClusterShear, "_proceed_generation"): return ExampleClusterShear( output_path=tmp_path, target_framework=Frameworks.COSMOSIS, **kwargs diff --git a/tests/app/examples/test_relocation.py b/tests/app/examples/test_relocation.py index 45be8cd49..85bd80f3f 100644 --- a/tests/app/examples/test_relocation.py +++ b/tests/app/examples/test_relocation.py @@ -30,7 +30,9 @@ params=CASES, ids=[f"{name}-{framework.value}" for name, framework in CASES], ) -def fixture_generated(request, tmp_path: Path) -> tuple[Path, Frameworks, str]: +def fixture_generated( + request: pytest.FixtureRequest, tmp_path: Path +) -> tuple[Path, Frameworks, str]: """Generate each example for each framework with the default options.""" name, framework = request.param example_cls, options = EXAMPLES[name] diff --git a/tests/app/test_experiment.py b/tests/app/test_experiment.py index 6984b60d8..52115a5cd 100644 --- a/tests/app/test_experiment.py +++ b/tests/app/test_experiment.py @@ -140,7 +140,9 @@ def test_load_experiment_invalid_yaml_raises_exception( with pytest.raises(Exception): Load(experiment_file=exp_file) - def test_load_experiment_prints_file_path(self, tmp_path: Path, capsys) -> None: + def test_load_experiment_prints_file_path( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that load prints the file path to console.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -201,7 +203,9 @@ def test_view_calls_print_factories(self, tmp_path: Path) -> None: class TestViewPrintFactories: """Tests for View._print_factories method.""" - def test_print_factories_displays_data_source(self, tmp_path: Path, capsys) -> None: + def test_print_factories_displays_data_source( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that _print_factories displays data source information.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -230,7 +234,9 @@ def test_print_factories_displays_data_source(self, tmp_path: Path, capsys) -> N assert "Data Source" in captured.out assert "data.sacc" in captured.out - def test_print_factories_displays_filters(self, tmp_path: Path, capsys) -> None: + def test_print_factories_displays_filters( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that filters are displayed when present.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -263,7 +269,7 @@ def test_print_factories_displays_filters(self, tmp_path: Path, capsys) -> None: assert "scale_cuts.yaml" in captured.out def test_print_factories_displays_weak_lensing( - self, tmp_path: Path, capsys + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """Test that weak lensing factories are displayed.""" exp_file = tmp_path / "exp.yaml" @@ -300,7 +306,7 @@ def test_print_factories_displays_weak_lensing( assert "src0" in captured.out def test_print_factories_displays_number_counts( - self, tmp_path: Path, capsys + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """Test that number counts factories are displayed.""" exp_file = tmp_path / "exp.yaml" @@ -335,7 +341,9 @@ def test_print_factories_displays_number_counts( assert "NumberCounts" in captured.out assert "lens0" in captured.out - def test_print_factories_displays_cmb(self, tmp_path: Path, capsys) -> None: + def test_print_factories_displays_cmb( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that CMB factories are displayed.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -367,7 +375,9 @@ def test_print_factories_displays_cmb(self, tmp_path: Path, capsys) -> None: assert "CMBConvergence" in captured.out assert "cmb_convergence" in captured.out - def test_print_factories_displays_systematics(self, tmp_path: Path, capsys) -> None: + def test_print_factories_displays_systematics( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that systematics are displayed.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -404,7 +414,7 @@ def test_print_factories_displays_systematics(self, tmp_path: Path, capsys) -> N assert "multiplicative_bias" in captured.out def test_print_factories_empty_systematics_shows_dash( - self, tmp_path: Path, capsys + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """Test that empty systematics list displays a dash.""" exp_file = tmp_path / "exp.yaml" @@ -439,7 +449,9 @@ def test_print_factories_empty_systematics_shows_dash( # The table should have dash for empty systematics assert "WeakLensing" in captured.out - def test_print_factories_multiple_factories(self, tmp_path: Path, capsys) -> None: + def test_print_factories_multiple_factories( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test displaying multiple factories of different types.""" exp_file = tmp_path / "exp.yaml" exp_data = { @@ -491,7 +503,9 @@ def test_print_factories_multiple_factories(self, tmp_path: Path, capsys) -> Non class TestFormatSysFunction: """Tests for the fmt_sys helper function within _print_factories.""" - def test_fmt_sys_with_multiple_systematics(self, tmp_path: Path, capsys) -> None: + def test_fmt_sys_with_multiple_systematics( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that multiple systematics are formatted correctly.""" exp_file = tmp_path / "exp.yaml" exp_data = { diff --git a/tests/conftest.py b/tests/conftest.py index 31ea7330d..09ac02881 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ import ast import sys from itertools import product -from typing import assert_never +from typing import Callable, assert_never import numpy as np import numpy.typing as npt @@ -43,6 +43,11 @@ from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ParamsMap, get_default_params_map from firecrown.utils import upper_triangle_indices +from tests._types import ( + RedshiftDistribution, + SaccGalaxyWindowedFixture, + WindowedAngularPowerSpectrum, +) # Helper function for creating AST ClassDef nodes across Python versions if sys.version_info >= (3, 12): @@ -83,7 +88,7 @@ def _make_class_def( ) -def pytest_addoption(parser): +def pytest_addoption(parser: pytest.Parser) -> None: """Add handling of firecrown-specific options for the pytest test runner. --runslow: used to run tests marked as slow, which are otherwise not run. @@ -100,7 +105,7 @@ def pytest_addoption(parser): ) -def pytest_configure(config): +def pytest_configure(config: pytest.Config) -> None: """Add new markers that can be set on pytest tests. Use the marker `slow` for any test that takes more than a second to run. @@ -110,7 +115,9 @@ def pytest_configure(config): config.addinivalue_line("markers", "slow: mark test as slow to run") -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems( + config: pytest.Config, items: list[pytest.Item] +) -> None: """Apply our special markers and option handling for pytest.""" if not config.getoption("--example"): @@ -120,7 +127,7 @@ def pytest_collection_modifyitems(config, items): _skip_tests(items, "slow", "need --runslow option to run") -def _skip_tests(items, keyword, reason): +def _skip_tests(items: list[pytest.Item], keyword: str, reason: str) -> None: """Helper method to skip tests based on a marker name.""" tests_to_skip = pytest.mark.skip(reason=reason) @@ -228,7 +235,7 @@ def fixture_tools_with_vanilla_cosmology() -> ModelingTools: @pytest.fixture(name="harmonic_bin_1") -def fixture_harmonic_bin_1(request) -> TomographicBin: +def fixture_harmonic_bin_1(request: pytest.FixtureRequest) -> TomographicBin: """Generate an TomographicBin object with 5 bins.""" return request.param @@ -327,7 +334,7 @@ def make_many_harmonic_bins() -> list[TomographicBin]: ], ids=["counts", "shear_t", "shear_minus", "shear_plus"], ) -def fixture_real_bin_1(request) -> TomographicBin: +def fixture_real_bin_1(request: pytest.FixtureRequest) -> TomographicBin: """Generate an TomographicBin object with 5 bins.""" x = TomographicBin( bin_name="bin_1", @@ -348,7 +355,7 @@ def fixture_real_bin_1(request) -> TomographicBin: ], ids=["counts", "shear_t", "shear_minus", "shear_plus"], ) -def fixture_real_bin_2(request) -> TomographicBin: +def fixture_real_bin_2(request: pytest.FixtureRequest) -> TomographicBin: """Generate an TomographicBin object with 3 bins.""" x = TomographicBin( bin_name="bin_2", @@ -584,9 +591,7 @@ def fixture_cluster_sacc_data() -> sacc.Sacc: @pytest.fixture(name="sacc_galaxy_cells_src0_src0", scope="module") -def fixture_sacc_galaxy_cells_src0_src0() -> ( - tuple[sacc.Sacc, npt.NDArray[np.float64], npt.NDArray[np.float64]] -): +def fixture_sacc_galaxy_cells_src0_src0() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data without window functions.""" sacc_data = sacc.Sacc() @@ -602,7 +607,13 @@ def fixture_sacc_galaxy_cells_src0_src0() -> ( cov = np.diag(np.ones_like(Cells) * 0.01) sacc_data.add_covariance(cov) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="src0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.SHEAR_E}, + ) + return sacc_data, expected_bin @pytest.fixture(name="sacc_galaxy_cells_src0_src1", scope="module") @@ -635,9 +646,7 @@ def fixture_sacc_galaxy_cells_src0_src1() -> tuple[ @pytest.fixture(name="sacc_galaxy_cells_lens0_lens0", scope="module") -def fixture_sacc_galaxy_cells_lens0_lens0() -> ( - tuple[sacc.Sacc, npt.NDArray[np.float64], npt.NDArray[np.float64]] -): +def fixture_sacc_galaxy_cells_lens0_lens0() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data without window functions.""" sacc_data = sacc.Sacc() @@ -653,7 +662,13 @@ def fixture_sacc_galaxy_cells_lens0_lens0() -> ( cov = np.diag(np.ones_like(Cells) * 0.01) sacc_data.add_covariance(cov) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="lens0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.COUNTS}, + ) + return sacc_data, expected_bin @pytest.fixture(name="sacc_galaxy_cells_lens0_lens1") @@ -684,9 +699,7 @@ def fixture_sacc_galaxy_cells_lens0_lens1() -> tuple[ @pytest.fixture(name="sacc_galaxy_xis_lens0_lens0") -def fixture_sacc_galaxy_xis_lens0_lens0() -> ( - tuple[sacc.Sacc, npt.NDArray[np.float64], npt.NDArray[np.float64]] -): +def fixture_sacc_galaxy_xis_lens0_lens0() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data without window functions.""" sacc_data = sacc.Sacc() @@ -702,7 +715,13 @@ def fixture_sacc_galaxy_xis_lens0_lens0() -> ( cov = np.diag(np.ones_like(xis) * 0.01) sacc_data.add_covariance(cov) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="lens0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.COUNTS}, + ) + return sacc_data, expected_bin @pytest.fixture(name="sacc_galaxy_xis_lens0_lens1") @@ -866,14 +885,7 @@ def fixture_sacc_galaxy_cells() -> tuple[ @pytest.fixture(name="sacc_galaxy_cwindows", scope="module") -def fixture_sacc_galaxy_cwindows() -> tuple[ - sacc.Sacc, - dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], - dict[ - tuple[TracerNames, str], - tuple[npt.NDArray[np.int64], npt.NDArray[np.float64], sacc.BandpowerWindow], - ], -]: +def fixture_sacc_galaxy_cwindows() -> SaccGalaxyWindowedFixture: """Fixture for a SACC data with window functions. Module-scoped because SACC objects are expensive to create and are @@ -888,11 +900,8 @@ def fixture_sacc_galaxy_cwindows() -> tuple[ src_bins_centers = np.linspace(0.25, 0.75, 5) lens_bins_centers = np.linspace(0.1, 0.6, 5) - tracers: dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]] = {} - tracer_pairs: dict[ - tuple[TracerNames, str], - tuple[npt.NDArray[np.int64], npt.NDArray[np.float64], sacc.BandpowerWindow], - ] = {} + tracers: dict[str, RedshiftDistribution] = {} + tracer_pairs: dict[tuple[TracerNames, str], WindowedAngularPowerSpectrum] = {} for i, mn in enumerate(src_bins_centers): dndz = np.exp(-0.5 * (z - mn) ** 2 / 0.05 / 0.05) @@ -1176,9 +1185,7 @@ def fixture_sacc_galaxy_cells_order_convention() -> sacc.Sacc: @pytest.fixture(name="sacc_galaxy_cells_src0_src0_no_data") -def fixture_sacc_galaxy_cells_src0_src0_no_data() -> ( - tuple[sacc.Sacc, np.ndarray, np.ndarray] -): +def fixture_sacc_galaxy_cells_src0_src0_no_data() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data without window functions.""" sacc_data = sacc.Sacc() @@ -1187,32 +1194,38 @@ def fixture_sacc_galaxy_cells_src0_src0_no_data() -> ( dndz = np.exp(-0.5 * (z - 0.5) ** 2 / 0.05 / 0.05) sacc_data.add_tracer("NZ", "src0", z, dndz) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="src0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.SHEAR_E}, + ) + return sacc_data, expected_bin -@pytest.fixture(name="sacc_galaxy_xis_lens0_lens0_no_data") -def fixture_sacc_galaxy_xis_lens0_lens0_no_data() -> ( - tuple[sacc.Sacc, np.ndarray, np.ndarray] -): - """Fixture for a SACC data without window functions.""" +@pytest.fixture(name="sacc_galaxy_lens0_no_two_point_data") +def fixture_sacc_galaxy_lens0_no_two_point_data() -> sacc.Sacc: + """Provide lens0 redshift information without measured two-point data. + + No angular separations or correlation measurements are stored. + + :returns: A SACC object containing only the lens0 redshift tracer. + """ sacc_data = sacc.Sacc() z = np.linspace(0, 1.0, 256) + 0.05 - thetas = np.linspace(0.0, 2.0 * np.pi, 20) dndz = np.exp(-0.5 * (z - 0.5) ** 2 / 0.05 / 0.05) sacc_data.add_tracer("NZ", "lens0", z, dndz) - return sacc_data, z, thetas + return sacc_data # Two-point related SACC data fixtures with window functions @pytest.fixture(name="sacc_galaxy_cells_src0_src0_window") -def fixture_sacc_galaxy_cells_src0_src0_window() -> ( - tuple[sacc.Sacc, np.ndarray, np.ndarray] -): +def fixture_sacc_galaxy_cells_src0_src0_window() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data with a window function.""" sacc_data = sacc.Sacc() @@ -1233,13 +1246,17 @@ def fixture_sacc_galaxy_cells_src0_src0_window() -> ( cov = np.diag(np.ones_like(Cells) * 0.01) sacc_data.add_covariance(cov) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="src0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.SHEAR_E}, + ) + return sacc_data, expected_bin @pytest.fixture(name="sacc_galaxy_cells_src0_src0_no_window") -def fixture_sacc_galaxy_cells_src0_src0_no_window() -> ( - tuple[sacc.Sacc, np.ndarray, np.ndarray] -): +def fixture_sacc_galaxy_cells_src0_src0_no_window() -> tuple[sacc.Sacc, TomographicBin]: """Fixture for a SACC data without a window function.""" sacc_data = sacc.Sacc() @@ -1255,7 +1272,13 @@ def fixture_sacc_galaxy_cells_src0_src0_no_window() -> ( cov = np.diag(np.ones_like(Cells) * 0.01) sacc_data.add_covariance(cov) - return sacc_data, z, dndz + expected_bin = TomographicBin( + bin_name="src0", + z=z.copy(), + dndz=dndz.copy(), + measurements={Galaxies.SHEAR_E}, + ) + return sacc_data, expected_bin @pytest.fixture(name="sacc_galaxy_xis_lens0_lens0_real") @@ -1321,7 +1344,7 @@ def fixture_tp_factory_missing_counts( # Optimized fixtures that eliminate "incompatible measurements" skips -def _discover_measurements_by_space(): +def _discover_measurements_by_space() -> tuple[list[Measurement], list[Measurement]]: """Automatically discover all measurements that support real/harmonic space. This function dynamically finds all measurement types from the enums, @@ -1355,7 +1378,10 @@ def _discover_measurements_by_space(): return real_measurements, harmonic_measurements -def _generate_compatible_pairs(measurements, compatibility_func): +def _generate_compatible_pairs( + measurements: list[Measurement], + compatibility_func: Callable[[Measurement, Measurement], bool], +) -> list[tuple[Measurement, Measurement]]: """Generate all valid measurement pairs for a given compatibility function.""" return [ (m1, m2) @@ -1376,7 +1402,9 @@ def _generate_compatible_pairs(measurements, compatibility_func): ) -def _create_measurement_pair_ids(pairs): +def _create_measurement_pair_ids( + pairs: list[tuple[Measurement, Measurement]], +) -> list[str]: """Create human-readable test IDs for measurement pairs.""" return [f"{m1.name.lower()}-{m2.name.lower()}" for m1, m2 in pairs] @@ -1386,7 +1414,9 @@ def _create_measurement_pair_ids(pairs): params=_VALID_REAL_MEASUREMENT_PAIRS, ids=_create_measurement_pair_ids(_VALID_REAL_MEASUREMENT_PAIRS), ) -def fixture_optimized_real_measurement_pair(request): +def fixture_optimized_real_measurement_pair( + request: pytest.FixtureRequest, +) -> tuple[Measurement, Measurement]: """Generate only valid real-space measurement pairs. Eliminates all "incompatible measurements" skips by pre-filtering @@ -1401,7 +1431,9 @@ def fixture_optimized_real_measurement_pair(request): params=_VALID_HARMONIC_MEASUREMENT_PAIRS, ids=_create_measurement_pair_ids(_VALID_HARMONIC_MEASUREMENT_PAIRS), ) -def fixture_optimized_harmonic_measurement_pair(request): +def fixture_optimized_harmonic_measurement_pair( + request: pytest.FixtureRequest, +) -> tuple[Galaxies | CMB, Galaxies | CMB]: """Generate only valid harmonic-space measurement pairs. Eliminates all "incompatible measurements" skips by pre-filtering @@ -1412,7 +1444,9 @@ def fixture_optimized_harmonic_measurement_pair(request): @pytest.fixture(name="optimized_real_two_point_xy") -def fixture_optimized_real_two_point_xy(optimized_real_measurement_pair) -> TwoPointXY: +def fixture_optimized_real_two_point_xy( + optimized_real_measurement_pair: tuple[Measurement, Measurement], +) -> TwoPointXY: """Generate TwoPointXY for real space with zero skipped tests. Uses auto-discovered valid measurement pairs, eliminating all @@ -1440,7 +1474,7 @@ def fixture_optimized_real_two_point_xy(optimized_real_measurement_pair) -> TwoP @pytest.fixture(name="optimized_harmonic_two_point_xy") def fixture_optimized_harmonic_two_point_xy( - optimized_harmonic_measurement_pair, + optimized_harmonic_measurement_pair: tuple[Galaxies | CMB, Galaxies | CMB], ) -> TwoPointXY: """Generate TwoPointXY for harmonic space with zero skipped tests. diff --git a/tests/connector/cobaya/test_model_likelihood.py b/tests/connector/cobaya/test_model_likelihood.py index 663b6dfc7..3afd8e70d 100644 --- a/tests/connector/cobaya/test_model_likelihood.py +++ b/tests/connector/cobaya/test_model_likelihood.py @@ -21,8 +21,10 @@ from firecrown.likelihood._likelihood import NamedParameters from firecrown.modeling_tools import ModelingTools +type CobayaParamsConfig = dict[str, float | dict[str, bool]] -def test_cobaya_ccl_initialize(): + +def test_cobaya_ccl_initialize() -> None: ccl_connector = LikelihoodConnector( info={ "firecrownIni": "tests/likelihood/lkdir/lkscript.py", @@ -35,7 +37,7 @@ def test_cobaya_ccl_initialize(): assert ccl_connector.distance_max_z == 4.0 -def test_cobaya_ccl_initialize_with_distance_max_z(): +def test_cobaya_ccl_initialize_with_distance_max_z() -> None: ccl_connector = LikelihoodConnector( info={ "firecrownIni": "tests/likelihood/lkdir/lkscript.py", @@ -47,7 +49,7 @@ def test_cobaya_ccl_initialize_with_distance_max_z(): assert ccl_connector.distance_max_z == 1210.0 -def test_cobaya_ccl_initialize_with_params(): +def test_cobaya_ccl_initialize_with_params() -> None: ccl_connector = LikelihoodConnector( info={ "firecrownIni": "tests/likelihood/lkdir/lkscript.py", @@ -61,7 +63,7 @@ def test_cobaya_ccl_initialize_with_params(): assert ccl_connector.input_style == "CAMB" -def test_compute_pyccl_args_options_default_no_extension(): +def test_compute_pyccl_args_options_default_no_extension() -> None: """With the default distance_max_z (4.0), no background extension is needed since it does not exceed the redshift already covered by spl.A_SPLINE_MIN (z~9).""" @@ -76,7 +78,7 @@ def test_compute_pyccl_args_options_default_no_extension(): assert z_bg.max() == pytest.approx(1.0 / spl.A_SPLINE_MIN - 1.0) -def test_compute_pyccl_args_options_extends_for_cmb_lensing(): +def test_compute_pyccl_args_options_extends_for_cmb_lensing() -> None: """A distance_max_z beyond spl.A_SPLINE_MIN's implicit z_max extends the background arrays out to that redshift, without the array's minimum scale factor landing exactly on spl.A_SPLINE_MINLOG (see the note in @@ -105,7 +107,7 @@ def test_compute_pyccl_args_options_extends_for_cmb_lensing(): assert z_spline_min == pytest.approx(z_bg_no_ext.max()) -def test_cobaya_likelihood_initialize(): +def test_cobaya_likelihood_initialize() -> None: lk_connector = LikelihoodConnector( info={ "firecrownIni": "tests/likelihood/lkdir/lkscript.py", @@ -117,7 +119,7 @@ def test_cobaya_likelihood_initialize(): assert lk_connector.firecrownIni == "tests/likelihood/lkdir/lkscript.py" -def test_cobaya_likelihood_initialize_with_params(): +def test_cobaya_likelihood_initialize_with_params() -> None: lk_connector = LikelihoodConnector( info={ "firecrownIni": "tests/likelihood/lkdir/lkscript.py", @@ -132,10 +134,10 @@ def test_cobaya_likelihood_initialize_with_params(): @pytest.fixture(name="fiducial_params") -def fixture_fiducial_params(): +def fixture_fiducial_params() -> CobayaParamsConfig: # Fiducial parameters for CAMB # These are the names expected by Cobaya. - fiducial_params = { + fiducial_params: CobayaParamsConfig = { "ombh2": 0.022, "omch2": 0.12, "H0": 68, @@ -148,7 +150,9 @@ def fixture_fiducial_params(): return fiducial_params -def test_cobaya_ccl_likelihood(fiducial_params): +def test_cobaya_ccl_likelihood( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -166,7 +170,9 @@ def test_cobaya_ccl_likelihood(fiducial_params): assert model_fiducial.logposterior({}).logpost == -3.0 -def test_resetting_after_exception_in_log_likelihood(fiducial_params): +def test_resetting_after_exception_in_log_likelihood( + fiducial_params: CobayaParamsConfig, +) -> None: fiducial_params.update({"pantheon_M": -19.0}) info_fiducial = { "params": fiducial_params, @@ -204,7 +210,9 @@ def test_resetting_after_exception_in_log_likelihood(fiducial_params): assert np.isfinite(model_fiducial.logposterior({}).logpost) -def test_parameterized_likelihood_missing(fiducial_params): +def test_parameterized_likelihood_missing( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -221,7 +229,9 @@ def test_parameterized_likelihood_missing(fiducial_params): _ = get_model(info_fiducial) -def test_parameterized_likelihood_wrong_type(fiducial_params): +def test_parameterized_likelihood_wrong_type( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -241,7 +251,9 @@ def test_parameterized_likelihood_wrong_type(fiducial_params): _ = get_model(info_fiducial) -def test_parameterized_likelihood_dict(fiducial_params): +def test_parameterized_likelihood_dict( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -260,7 +272,9 @@ def test_parameterized_likelihood_dict(fiducial_params): assert model_fiducial.logposterior({}).logpost == -1.5 -def test_parameterized_likelihood_namedparameters(fiducial_params): +def test_parameterized_likelihood_namedparameters( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -281,7 +295,9 @@ def test_parameterized_likelihood_namedparameters(fiducial_params): assert model_fiducial.logposterior({}).logpost == -1.5 -def test_sampler_parameter_likelihood_missing(fiducial_params): +def test_sampler_parameter_likelihood_missing( + fiducial_params: CobayaParamsConfig, +) -> None: info_fiducial = { "params": fiducial_params, "likelihood": { @@ -299,7 +315,9 @@ def test_sampler_parameter_likelihood_missing(fiducial_params): _ = get_model(info_fiducial) -def test_sampler_parameter_likelihood(fiducial_params): +def test_sampler_parameter_likelihood( + fiducial_params: CobayaParamsConfig, +) -> None: fiducial_params.update({"my_prefix_sampler_param0": 1.0}) info_fiducial = { "params": fiducial_params, @@ -319,7 +337,9 @@ def test_sampler_parameter_likelihood(fiducial_params): assert model_fiducial.logposterior({}).logpost == -2.1 -def test_derived_parameter_likelihood(fiducial_params): +def test_derived_parameter_likelihood( + fiducial_params: CobayaParamsConfig, +) -> None: fiducial_params.update({"derived_section__derived_param0": {"derived": True}}) info_fiducial = { "params": fiducial_params, @@ -341,7 +361,9 @@ def test_derived_parameter_likelihood(fiducial_params): assert logpost.derived[0] == 1.0 -def _factory_as(_: NamedParameters): +def _factory_as( + _: NamedParameters, +) -> tuple[ConstGaussian, ModelingTools]: return ConstGaussian([stat.TrivialStatistic()]), ModelingTools( ccl_factory=ccl_factory.CCLFactory( amplitude_parameter=ccl_factory.PoweSpecAmplitudeParameter.AS @@ -349,7 +371,9 @@ def _factory_as(_: NamedParameters): ) -def _factory_sigma8(_: NamedParameters): +def _factory_sigma8( + _: NamedParameters, +) -> tuple[ConstGaussian, ModelingTools]: return ConstGaussian([stat.TrivialStatistic()]), ModelingTools( ccl_factory=ccl_factory.CCLFactory( amplitude_parameter=ccl_factory.PoweSpecAmplitudeParameter.SIGMA8 @@ -357,7 +381,7 @@ def _factory_sigma8(_: NamedParameters): ) -def test_likelihood_connector_from_module(): +def test_likelihood_connector_from_module() -> None: name = "my_dummy_likelihood_module" factory_full = f"{name}.factory" mod = types.ModuleType(factory_full) @@ -378,7 +402,7 @@ def test_likelihood_connector_from_module(): assert "sigma8" in lk_connector.get_requirements() -def test_get_can_support_params_with_map_none(): +def test_get_can_support_params_with_map_none() -> None: """Test get_can_support_params when map is None (non-DEFAULT creation mode).""" # Create a connector that uses PURE_CCL_MODE, which keeps map=None lk_connector = LikelihoodConnector( @@ -396,7 +420,7 @@ def test_get_can_support_params_with_map_none(): assert supported_params == [] -def test_logp_with_pure_ccl_mode(): +def test_logp_with_pure_ccl_mode() -> None: """Test logp method with PURE_CCL_MODE (non-DEFAULT creation mode).""" # Pure CCL mode requires different parameters than the CAMB-based DEFAULT mode pure_ccl_params = { @@ -431,7 +455,7 @@ def test_logp_with_pure_ccl_mode(): assert logpost.logpost == -3.0 -def test_missing_input_style_assertion(): +def test_missing_input_style_assertion() -> None: """Test assertion failure when input_style is None in DEFAULT mode.""" # Create a connector with DEFAULT mode but no input_style # This should trigger the assertion at line 113: assert self.input_style @@ -444,7 +468,7 @@ def test_missing_input_style_assertion(): ) -def test_get_requirements_different_modes(): +def test_get_requirements_different_modes() -> None: """Test get_requirements with different CCL creation modes.""" # Test with DEFAULT mode (should include external observables) lk_connector_default = LikelihoodConnector( @@ -478,7 +502,7 @@ def test_get_requirements_different_modes(): assert "Hubble" not in requirements_pure -def test_initialize_without_build_parameters_attribute(): +def test_initialize_without_build_parameters_attribute() -> None: """Test initialize() when build_parameters attribute doesn't exist. This covers the case where hasattr(self, 'build_parameters') is False, diff --git a/tests/connector/cosmosis/test_cosmosis_module.py b/tests/connector/cosmosis/test_cosmosis_module.py index ef8e84925..7fe2b98cd 100644 --- a/tests/connector/cosmosis/test_cosmosis_module.py +++ b/tests/connector/cosmosis/test_cosmosis_module.py @@ -10,9 +10,10 @@ import pytest import numpy as np -import pyccl from cosmosis.datablock import DataBlock, option_section, names as section_names +import pyccl +from firecrown.connector.mapping import MappingCosmoSIS from firecrown.likelihood._likelihood import NamedParameters from firecrown.connector.cosmosis.likelihood import ( FirecrownLikelihood, @@ -109,7 +110,7 @@ def fixture_config_with_two_point_real() -> DataBlock: @pytest.fixture(name="ccl_mode", params=["default", "pure_ccl"]) -def fixture_ccl_mode(request) -> DataBlock: +def fixture_ccl_mode(request: pytest.FixtureRequest) -> str: return request.param @@ -280,7 +281,9 @@ def fixture_sample_without_M(minimal_sample: DataBlock) -> DataBlock: return minimal_sample -def test_extract_section_gets_named_parameters(defective_module_config: DataBlock): +def test_extract_section_gets_named_parameters( + defective_module_config: DataBlock, +) -> None: params = extract_section(defective_module_config, "module_options") assert isinstance(params, NamedParameters) assert params.get_string("likelihood_source") == str( @@ -288,16 +291,18 @@ def test_extract_section_gets_named_parameters(defective_module_config: DataBloc ) -def test_extract_section_raises_on_missing_section(defective_module_config: DataBlock): +def test_extract_section_raises_on_missing_section( + defective_module_config: DataBlock, +) -> None: with pytest.raises(RuntimeError, match="Datablock section `xxx' does not exist"): _ = extract_section(defective_module_config, "xxx") @pytest.mark.parametrize("ccl_mode", ["pure_ccl"], indirect=True) def test_wrong_use_of_camb_module_with_ccl_not_in_default_mode( - config_with_two_point_harmonic, + config_with_two_point_harmonic: DataBlock, ccl_mode: str, -): +) -> None: config_with_two_point_harmonic.put_string("camb", "cow", "moo") assert ccl_mode == "pure_ccl" with pytest.raises( @@ -311,27 +316,27 @@ def test_wrong_use_of_camb_module_with_ccl_not_in_default_mode( _ = FirecrownLikelihood(config_with_two_point_harmonic) -def test_parameterless_module_construction(minimal_module_config): +def test_parameterless_module_construction(minimal_module_config: DataBlock) -> None: """Make sure we can create a CosmoSIS likelihood modules that does not introduce any new parameters.""" module = FirecrownLikelihood(minimal_module_config) assert module.sampling_sections == [] -def test_missing_required_parameter(defective_module_config): +def test_missing_required_parameter(defective_module_config: DataBlock) -> None: """Make sure that a missing required parameter entails the expected failure.""" with pytest.raises(KeyError): _ = FirecrownLikelihood(defective_module_config) -def test_initialize_minimal_module(minimal_firecrown_mod: FirecrownLikelihood): +def test_initialize_minimal_module(minimal_firecrown_mod: FirecrownLikelihood) -> None: assert isinstance(minimal_firecrown_mod, FirecrownLikelihood) def test_execute_missing_cosmological_parameters( minimal_firecrown_mod: FirecrownLikelihood, -): +) -> None: no_cosmo_params = DataBlock() with pytest.raises( RuntimeError, @@ -343,7 +348,7 @@ def test_execute_missing_cosmological_parameters( @pytest.mark.parametrize("ccl_mode", ["default"], indirect=True) def test_execute_with_cosmo( minimal_firecrown_mod: FirecrownLikelihood, minimal_sample: DataBlock, ccl_mode: str -): +) -> None: assert ccl_mode == "default" assert minimal_firecrown_mod.execute(minimal_sample) == 0 assert minimal_sample[section_names.likelihoods, "firecrown_like"] == -3.0 @@ -354,7 +359,7 @@ def test_execute_with_derived_parameters( firecrown_mod_with_derived_parameters: FirecrownLikelihood, minimal_sample: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" assert firecrown_mod_with_derived_parameters.execute(minimal_sample) == 0 assert minimal_sample.get_double("derived_section", "derived_param0") == 1.0 @@ -364,7 +369,7 @@ def test_execute_with_derived_parameters( def test_module_init_with_missing_sampling_sections( config_with_const_gaussian_missing_sampling_parameters_sections: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" with pytest.raises(RuntimeError, match=r"\['pantheon_M'\]"): s = config_with_const_gaussian_missing_sampling_parameters_sections.to_string() @@ -396,7 +401,7 @@ def fixture_working_config_for_const_gaussian( def test_module_init_with_wrong_sampling_sections( config_with_wrong_sampling_sections: DataBlock, -): +) -> None: mod = FirecrownLikelihood(config_with_wrong_sampling_sections) assert isinstance(mod, FirecrownLikelihood) @@ -406,7 +411,7 @@ def test_module_exec_with_wrong_sampling_sections( config_with_wrong_sampling_sections: DataBlock, sample_with_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" mod = FirecrownLikelihood(config_with_wrong_sampling_sections) with pytest.raises( @@ -420,7 +425,7 @@ def test_module_exec_missing_parameter_in_sampling_sections( firecrown_mod_with_const_gaussian: FirecrownLikelihood, sample_without_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" with pytest.raises(RuntimeError, match="`supernova_parameters`") as exc: _ = firecrown_mod_with_const_gaussian.execute(sample_without_M) @@ -435,7 +440,7 @@ def test_module_exec_working( firecrown_mod_with_const_gaussian: FirecrownLikelihood, sample_with_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" assert firecrown_mod_with_const_gaussian.execute(sample_with_M) == 0 assert sample_with_M.get_double("likelihoods", "firecrown_like") < 0.0 @@ -446,7 +451,7 @@ def test_execute_function( firecrown_mod_with_const_gaussian: FirecrownLikelihood, sample_with_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" assert execute(sample_with_M, firecrown_mod_with_const_gaussian) == 0 assert sample_with_M.get_double("likelihoods", "firecrown_like") < 0.0 @@ -457,7 +462,7 @@ def test_module_exec_with_two_point_real( firecrown_mod_with_two_point_real: FirecrownLikelihood, sample_with_lens0_bias: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" assert firecrown_mod_with_two_point_real.execute(sample_with_lens0_bias) == 0 @@ -513,7 +518,7 @@ def test_module_exec_with_two_point_real( def test_module_exec_with_two_point_harmonic( firecrown_mod_with_two_point_harmonic: FirecrownLikelihood, sample_with_lens0_bias: DataBlock, -): +) -> None: assert firecrown_mod_with_two_point_harmonic.execute(sample_with_lens0_bias) == 0 # CosmoSIS always writes the output to the same section, so we can @@ -565,7 +570,7 @@ def test_module_exec_with_two_point_harmonic( ) -def test_mapping_cosmosis_background(mapping_cosmosis): +def test_mapping_cosmosis_background(mapping_cosmosis: MappingCosmoSIS) -> None: block = DataBlock() background_d_m = np.geomspace(0.1, 10.0, 100) @@ -593,7 +598,7 @@ def test_mapping_cosmosis_background(mapping_cosmosis): assert "pk_nonlin" not in ccl_args -def test_mapping_cosmosis_pk_linear(mapping_cosmosis): +def test_mapping_cosmosis_pk_linear(mapping_cosmosis: MappingCosmoSIS) -> None: block = DataBlock() block.put_double_array_1d("distances", "d_m", np.geomspace(0.1, 10.0, 100)) @@ -630,7 +635,7 @@ def test_mapping_cosmosis_pk_linear(mapping_cosmosis): ) -def test_mapping_cosmosis_pk_nonlin(mapping_cosmosis): +def test_mapping_cosmosis_pk_nonlin(mapping_cosmosis: MappingCosmoSIS) -> None: block = DataBlock() block.put_double_array_1d("distances", "d_m", np.geomspace(0.1, 10.0, 100)) @@ -668,7 +673,7 @@ def test_mapping_cosmosis_pk_nonlin(mapping_cosmosis): ) -def test_form_error_message_with_sampling_sections(): +def test_form_error_message_with_sampling_sections() -> None: sampling_sections = ["section1", "section2"] exc = MissingSamplerParameterError("missing_param") expected_msg = ( @@ -680,7 +685,7 @@ def test_form_error_message_with_sampling_sections(): assert form_error_message(sampling_sections, exc) == expected_msg -def test_form_error_message_with_empty_sampling_sections(): +def test_form_error_message_with_empty_sampling_sections() -> None: sampling_sections: list[str] = [] exc = MissingSamplerParameterError("missing_param") # flake8: noqa @@ -693,7 +698,7 @@ def test_form_error_message_with_empty_sampling_sections(): assert form_error_message(sampling_sections, exc) == expected_msg -def test_form_error_message_with_single_sampling_section(): +def test_form_error_message_with_single_sampling_section() -> None: sampling_sections = ["section1"] exc = MissingSamplerParameterError("missing_param") # flake8: noqa @@ -706,13 +711,15 @@ def test_form_error_message_with_single_sampling_section(): assert form_error_message(sampling_sections, exc) == expected_msg -def test_form_error_message_with_missing_sampler_parameter_error(): +def test_form_error_message_with_missing_sampler_parameter_error() -> None: sampling_sections = ["section1", "section2"] exc = MissingSamplerParameterError("missing_param") assert "missing_param" in form_error_message(sampling_sections, exc) -def test_same_param_names_in_different_sections_failure(sample_with_M: DataBlock): +def test_same_param_names_in_different_sections_failure( + sample_with_M: DataBlock, +) -> None: sample_with_M.put_double("section1", "a", 1.0) sample_with_M.put_double("section2", "a", 10.0) with pytest.raises( @@ -730,7 +737,7 @@ def test_resetting_after_exception_in_log_likelihood( firecrown_mod_with_const_gaussian: FirecrownLikelihood, sample_with_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" msg = "Test exception in log-likelihood" warn_msg = ( @@ -760,7 +767,7 @@ def test_gaussfamily_special_case( firecrown_mod_with_const_gaussian: FirecrownLikelihood, sample_with_M: DataBlock, ccl_mode: str, -): +) -> None: assert ccl_mode == "default" msg = "Error CCL_ERROR_SPLINE: fake_ccl_.c:119;" warn_msg = f"CCL error:.*\n.*{msg}" diff --git a/tests/connector/numcosmo/conftest.py b/tests/connector/numcosmo/conftest.py index 7c6c6d63e..d36b73ae2 100644 --- a/tests/connector/numcosmo/conftest.py +++ b/tests/connector/numcosmo/conftest.py @@ -6,16 +6,21 @@ import pytest from numcosmo_py import GObject, Nc, Ncm +from tests._types import NumCosmoCosmologyFixture + +# NumCosmo's introspected static constructors and enum members confuse Pylint. +# Keep the corresponding suppressions local to the affected calls below. + @pytest.fixture(name="numcosmo_cosmo_xcdm") -def fixture_numcosmo_cosmo_xcdm(): +def fixture_numcosmo_cosmo_xcdm() -> NumCosmoCosmologyFixture: """Create a NumCosmo cosmology instance of XCDM.""" - prim = Nc.HIPrimPowerLaw.new() + prim = Nc.HIPrimPowerLaw.new() # pylint: disable=no-value-for-parameter prim.param_set_by_name( # pylint: disable=no-member "ln10e10ASA", math.log(1.0e10 * 2.0e-09) ) prim.param_set_by_name("n_SA", 0.971) # pylint: disable=no-member - reion = Nc.HIReionCamb.new() + reion = Nc.HIReionCamb.new() # pylint: disable=no-value-for-parameter bbn = Nc.BBNParametrized.new() bbn.param_set_by_name("Yp", 0.2454) # pylint: disable=no-member @@ -31,7 +36,9 @@ def fixture_numcosmo_cosmo_xcdm(): reion.set_z_from_tau(0.0561) # pylint: disable=no-member - p_ml = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + p_ml = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) p_mnl = Nc.PowspecMNLHaloFit.new(p_ml, 3.0, 1.0e-5) dist = Nc.Distance.new(6.0) dist.comoving_distance_spline.set_reltol(1.0e-5) # pylint: disable=no-member @@ -40,14 +47,14 @@ def fixture_numcosmo_cosmo_xcdm(): @pytest.fixture(name="numcosmo_cosmo_xcdm_no_nu") -def fixture_numcosmo_cosmo_xcdm_no_nu(): +def fixture_numcosmo_cosmo_xcdm_no_nu() -> NumCosmoCosmologyFixture: """Create a NumCosmo cosmology instance of XCDM without neutrinos.""" - prim = Nc.HIPrimPowerLaw.new() + prim = Nc.HIPrimPowerLaw.new() # pylint: disable=no-value-for-parameter prim.param_set_by_name( # pylint: disable=no-member "ln10e10ASA", math.log(1.0e10 * 2.0e-09) ) prim.param_set_by_name("n_SA", 0.971) # pylint: disable=no-member - reion = Nc.HIReionCamb.new() + reion = Nc.HIReionCamb.new() # pylint: disable=no-value-for-parameter bbn = Nc.BBNParametrized.new() bbn.param_set_by_name("Yp", 0.2454) # pylint: disable=no-member @@ -62,7 +69,9 @@ def fixture_numcosmo_cosmo_xcdm_no_nu(): reion.set_z_from_tau(0.0561) # pylint: disable=no-member - p_ml = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + p_ml = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) p_mnl = Nc.PowspecMNLHaloFit.new(p_ml, 3.0, 1.0e-5) dist = Nc.Distance.new(6.0) dist.comoving_distance_spline.set_reltol(1.0e-5) # pylint: disable=no-member @@ -71,16 +80,16 @@ def fixture_numcosmo_cosmo_xcdm_no_nu(): @pytest.fixture(name="numcosmo_cosmo_cpl") -def fixture_numcosmo_cosmo_cpl(): +def fixture_numcosmo_cosmo_cpl() -> NumCosmoCosmologyFixture: """Create a NumCosmo cosmology instance using CPL parametrization for the dark energy equation of state.""" - prim = Nc.HIPrimPowerLaw.new() + prim = Nc.HIPrimPowerLaw.new() # pylint: disable=no-value-for-parameter prim.param_set_by_name( # pylint: disable=no-member "ln10e10ASA", math.log(1.0e10 * 2.0e-09) ) prim.param_set_by_name("n_SA", 0.971) # pylint: disable=no-member - reion = Nc.HIReionCamb.new() + reion = Nc.HIReionCamb.new() # pylint: disable=no-value-for-parameter bbn = Nc.BBNParametrized.new() bbn.param_set_by_name("Yp", 0.2454) # pylint: disable=no-member @@ -96,7 +105,9 @@ def fixture_numcosmo_cosmo_cpl(): reion.set_z_from_tau(0.0561) # pylint: disable=no-member - p_ml = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + p_ml = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) p_mnl = Nc.PowspecMNLHaloFit.new(p_ml, 3.0, 1.0e-5) dist = Nc.Distance.new(6.0) dist.comoving_distance_spline.set_reltol(1.0e-5) # pylint: disable=no-member @@ -105,7 +116,7 @@ def fixture_numcosmo_cosmo_cpl(): @pytest.fixture(name="nc_model_trivial", scope="session") -def fixture_nc_model_trivial(): +def fixture_nc_model_trivial() -> type[Ncm.Model]: """Create a NumCosmoModel instance.""" my_model_trivial_yaml = r""" NcmModelBuilder: @@ -123,7 +134,7 @@ def fixture_nc_model_trivial(): default-value: 0.1 fit-type: 0 """ - ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) + ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) # pylint: disable=no-member mb_model: Ncm.ModelBuilder = cast( Ncm.ModelBuilder, ser.from_yaml(my_model_trivial_yaml), # pylint: disable=no-member diff --git a/tests/connector/numcosmo/test_numcosmo_connector.py b/tests/connector/numcosmo/test_numcosmo_connector.py index c032e2b7d..bc9f2117c 100644 --- a/tests/connector/numcosmo/test_numcosmo_connector.py +++ b/tests/connector/numcosmo/test_numcosmo_connector.py @@ -3,22 +3,28 @@ import pytest from numcosmo_py import GObject, Nc, Ncm +from firecrown.connector.mapping import Mapping from firecrown.connector.numcosmo import ( MappingNumCosmo, NumCosmoData, NumCosmoFactory, NumCosmoGaussCov, + create_params_map, ) from firecrown.likelihood._likelihood import ( Likelihood, NamedParameters, ) +from firecrown.modeling_tools import CCLCreationMode +from firecrown.updatable import ParamsMap -Ncm.cfg_init() +# Pylint mistakes NumCosmo's introspected static functions for instance methods. +# Keep no-value-for-parameter suppressions local to the affected calls below. +Ncm.cfg_init() # pylint: disable=no-value-for-parameter @pytest.fixture(name="factory_plain") -def fixture_factory_plain(): +def fixture_factory_plain() -> NumCosmoFactory: """Create a NumCosmoFactory instance.""" map_cosmo = MappingNumCosmo(dist=Nc.Distance.new(6.0)) build_parameters = NamedParameters() @@ -31,7 +37,7 @@ def fixture_factory_plain(): @pytest.fixture(name="factory_const_gauss") -def fixture_factory_const_gauss(): +def fixture_factory_const_gauss() -> NumCosmoFactory: """Create a NumCosmoFactory instance.""" map_cosmo = MappingNumCosmo(dist=Nc.Distance.new(6.0)) build_parameters = NamedParameters() @@ -43,12 +49,18 @@ def fixture_factory_const_gauss(): ) -def test_wrong_use_of_power_spectra_with_ccl_not_in_default_mode(): +def test_wrong_use_of_power_spectra_with_ccl_not_in_default_mode() -> None: map_cosmo = MappingNumCosmo( dist=Nc.Distance.new(6.0), - p_ml=Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()), + p_ml=Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ), p_mnl=Nc.PowspecMNLHaloFit.new( - Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()), 3.0, 1.0e-4 + Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ), + 3.0, + 1.0e-4, ), ) build_parameters = NamedParameters({"projection": "harmonic"}) @@ -61,21 +73,23 @@ def test_wrong_use_of_power_spectra_with_ccl_not_in_default_mode(): ) -def test_numcosmo_connector_plain(factory_plain): +def test_numcosmo_connector_plain(factory_plain: NumCosmoFactory) -> None: """Test the NumCosmo connector.""" assert factory_plain.get_data() is not None assert factory_plain.get_mapping() is not None -def test_numcosmo_connector_const_gauss(factory_const_gauss): +def test_numcosmo_connector_const_gauss(factory_const_gauss: NumCosmoFactory) -> None: """Test the NumCosmo connector.""" assert factory_const_gauss.get_data() is not None assert factory_const_gauss.get_mapping() is not None -def test_numcosmo_factory_plain_get_firecrown_likelihood(factory_plain): +def test_numcosmo_factory_plain_get_firecrown_likelihood( + factory_plain: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" assert factory_plain.get_data() is not None @@ -86,7 +100,9 @@ def test_numcosmo_factory_plain_get_firecrown_likelihood(factory_plain): assert isinstance(fc_likelihood, Likelihood) -def test_numcosmo_factory_const_gauss_get_firecrown_likelihood(factory_const_gauss): +def test_numcosmo_factory_const_gauss_get_firecrown_likelihood( + factory_const_gauss: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" assert factory_const_gauss.get_data() is not None @@ -97,7 +113,7 @@ def test_numcosmo_factory_const_gauss_get_firecrown_likelihood(factory_const_gau assert isinstance(fc_likelihood, Likelihood) -def test_numcosmo_factory_plain_get_properties(factory_plain): +def test_numcosmo_factory_plain_get_properties(factory_plain: NumCosmoFactory) -> None: """Test the NumCosmo connector.""" nc_data = factory_plain.get_data() @@ -110,7 +126,9 @@ def test_numcosmo_factory_plain_get_properties(factory_plain): assert isinstance(nc_data.likelihood_build_parameters, Ncm.VarDict) -def test_numcosmo_factory_const_gauss_get_properties(factory_const_gauss): +def test_numcosmo_factory_const_gauss_get_properties( + factory_const_gauss: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_const_gauss.get_data() @@ -126,7 +144,9 @@ def test_numcosmo_factory_const_gauss_get_properties(factory_const_gauss): assert isinstance(nc_data.likelihood_build_parameters, Ncm.VarDict) -def test_numcosmo_factory_plain_set_properties_order1(factory_plain): +def test_numcosmo_factory_plain_set_properties_order1( + factory_plain: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_plain.get_data() @@ -140,7 +160,9 @@ def test_numcosmo_factory_plain_set_properties_order1(factory_plain): assert isinstance(old_fc_likelihood, Likelihood) nc_data.likelihood_source = "tests/likelihood/lkdir/lkscript.py" - nc_data.likelihood_build_parameters = Ncm.VarDict.new() + nc_data.likelihood_build_parameters = ( + Ncm.VarDict.new() # pylint: disable=no-value-for-parameter + ) fc_likelihood = nc_data.likelihood assert fc_likelihood is not None @@ -148,7 +170,9 @@ def test_numcosmo_factory_plain_set_properties_order1(factory_plain): assert fc_likelihood != old_fc_likelihood -def test_numcosmo_factory_plain_set_properties_order2(factory_plain): +def test_numcosmo_factory_plain_set_properties_order2( + factory_plain: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_plain.get_data() @@ -161,7 +185,9 @@ def test_numcosmo_factory_plain_set_properties_order2(factory_plain): assert old_fc_likelihood is not None assert isinstance(old_fc_likelihood, Likelihood) - nc_data.likelihood_build_parameters = Ncm.VarDict.new() + nc_data.likelihood_build_parameters = ( + Ncm.VarDict.new() # pylint: disable=no-value-for-parameter + ) nc_data.likelihood_source = "tests/likelihood/lkdir/lkscript.py" fc_likelihood = nc_data.likelihood @@ -170,7 +196,9 @@ def test_numcosmo_factory_plain_set_properties_order2(factory_plain): assert fc_likelihood != old_fc_likelihood -def test_numcosmo_factory_plain_set_properties_empty_build_parameters(factory_plain): +def test_numcosmo_factory_plain_set_properties_empty_build_parameters( + factory_plain: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_plain.get_data() @@ -192,7 +220,9 @@ def test_numcosmo_factory_plain_set_properties_empty_build_parameters(factory_pl assert fc_likelihood != old_fc_likelihood -def test_numcosmo_factory_const_gauss_set_properties_order1(factory_const_gauss): +def test_numcosmo_factory_const_gauss_set_properties_order1( + factory_const_gauss: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_const_gauss.get_data() @@ -208,7 +238,9 @@ def test_numcosmo_factory_const_gauss_set_properties_order1(factory_const_gauss) nc_data.likelihood_source = ( "tests/likelihood/gauss_family/lkscript_const_gaussian.py" ) - nc_data.likelihood_build_parameters = Ncm.VarDict.new() + nc_data.likelihood_build_parameters = ( + Ncm.VarDict.new() # pylint: disable=no-value-for-parameter + ) fc_likelihood = nc_data.likelihood assert fc_likelihood is not None @@ -216,7 +248,9 @@ def test_numcosmo_factory_const_gauss_set_properties_order1(factory_const_gauss) assert fc_likelihood != old_fc_likelihood -def test_numcosmo_factory_const_gauss_set_properties_order2(factory_const_gauss): +def test_numcosmo_factory_const_gauss_set_properties_order2( + factory_const_gauss: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_const_gauss.get_data() @@ -229,7 +263,9 @@ def test_numcosmo_factory_const_gauss_set_properties_order2(factory_const_gauss) assert old_fc_likelihood is not None assert isinstance(old_fc_likelihood, Likelihood) - nc_data.likelihood_build_parameters = Ncm.VarDict.new() + nc_data.likelihood_build_parameters = ( + Ncm.VarDict.new() # pylint: disable=no-value-for-parameter + ) nc_data.likelihood_source = ( "tests/likelihood/gauss_family/lkscript_const_gaussian.py" ) @@ -241,8 +277,8 @@ def test_numcosmo_factory_const_gauss_set_properties_order2(factory_const_gauss) def test_numcosmo_factory_const_gauss_set_properties_empty_build_parameters( - factory_const_gauss, -): + factory_const_gauss: NumCosmoFactory, +) -> None: """Test the NumCosmo connector.""" nc_data = factory_const_gauss.get_data() @@ -266,7 +302,7 @@ def test_numcosmo_factory_const_gauss_set_properties_empty_build_parameters( assert fc_likelihood != old_fc_likelihood -def test_empty_plain_data(): +def test_empty_plain_data() -> None: """Test the NumCosmo connector.""" nc_data = NumCosmoData() @@ -274,7 +310,7 @@ def test_empty_plain_data(): assert nc_data.likelihood_build_parameters is None -def test_empty_gauss_cov_data(): +def test_empty_gauss_cov_data() -> None: """Test the NumCosmo connector.""" nc_data = NumCosmoGaussCov() @@ -282,12 +318,8 @@ def test_empty_gauss_cov_data(): assert nc_data.likelihood_build_parameters is None -def test_create_params_map_with_mapping(): +def test_create_params_map_with_mapping() -> None: """Test create_params_map function when mapping is provided.""" - from firecrown.connector.mapping import Mapping - from firecrown.connector.numcosmo import create_params_map - from firecrown.updatable import ParamsMap - # Create a simple mock model and mset model_name = "test_model" model_builder = Ncm.ModelBuilder.new( @@ -347,11 +379,8 @@ def test_create_params_map_with_mapping(): assert params_map["h"] == 0.7 -def test_create_params_map_without_mapping(): +def test_create_params_map_without_mapping() -> None: """Test create_params_map function when mapping is None.""" - from firecrown.connector.numcosmo import create_params_map - from firecrown.updatable import ParamsMap - # Create a simple mock model and mset model_name = "test_model2" model_builder = Ncm.ModelBuilder.new( @@ -393,7 +422,7 @@ def test_create_params_map_without_mapping(): assert "h" not in params_map -def test_numcosmo_data_pure_ccl_mode(): +def test_numcosmo_data_pure_ccl_mode() -> None: """Test NumCosmoData.do_prepare with PURE_CCL_MODE (non-DEFAULT creation mode).""" # This test covers lines 574-577 (the 'else' branch in do_prepare) build_parameters = NamedParameters() @@ -408,8 +437,6 @@ def test_numcosmo_data_pure_ccl_mode(): assert isinstance(nc_data, NumCosmoData) # Verify that the CCL factory is in PURE_CCL_MODE - from firecrown.modeling_tools import CCLCreationMode - assert nc_data.tools.ccl_factory.creation_mode == CCLCreationMode.PURE_CCL_MODE # Create a simple model for testing @@ -455,7 +482,10 @@ def test_numcosmo_data_pure_ccl_mode(): model.params_set_default_ftype() # Create a basic cosmology model as well - cosmo = Nc.HICosmoDEXcdm(prim=Nc.HIPrimPowerLaw.new(), reion=Nc.HIReionCamb.new()) + cosmo = Nc.HICosmoDEXcdm( + prim=Nc.HIPrimPowerLaw.new(), # pylint: disable=no-value-for-parameter + reion=Nc.HIReionCamb.new(), # pylint: disable=no-value-for-parameter + ) mset = Ncm.MSet() mset.set(cosmo) @@ -470,7 +500,7 @@ def test_numcosmo_data_pure_ccl_mode(): assert nc_data.dof == nc_data.len - mset.fparams_len() -def test_numcosmo_gauss_cov_none_mapping(): +def test_numcosmo_gauss_cov_none_mapping() -> None: """Test NumCosmoGaussCov._will_calculate_power_spectra with None mapping.""" # This test covers line 677 where _nc_mapping is None and returns False build_parameters = NamedParameters() @@ -484,20 +514,21 @@ def test_numcosmo_gauss_cov_none_mapping(): nc_data = factory.get_data() assert isinstance(nc_data, NumCosmoGaussCov) - # Verify that _nc_mapping is None - assert nc_data._nc_mapping is None + # These assertions intentionally exercise the internal no-mapping path. + assert nc_data._nc_mapping is None # pylint: disable=protected-access - # Test _will_calculate_power_spectra method indirectly - # When _nc_mapping is None, _will_calculate_power_spectra should return False - # This is tested through the _configure_object method which calls it - assert nc_data._will_calculate_power_spectra() is False + # With no mapping, this internal method must not request power spectra. + assert ( + nc_data._will_calculate_power_spectra() # pylint: disable=protected-access + is False + ) # Verify the object was configured correctly assert nc_data.len > 0 assert nc_data.dof > 0 -def test_numcosmo_gauss_cov_pure_ccl_mode(): +def test_numcosmo_gauss_cov_pure_ccl_mode() -> None: """Test do_prepare with PURE_CCL_MODE (non-DEFAULT creation mode).""" # This test covers lines 887-891 (the 'else' branch in do_prepare) build_parameters = NamedParameters({"projection": "harmonic"}) @@ -512,13 +543,11 @@ def test_numcosmo_gauss_cov_pure_ccl_mode(): assert isinstance(nc_data, NumCosmoGaussCov) # Verify that the CCL factory is in PURE_CCL_MODE - from firecrown.modeling_tools import CCLCreationMode - assert nc_data.tools.ccl_factory.creation_mode == CCLCreationMode.PURE_CCL_MODE # Verify that _nc_mapping is None (this triggers assert _nc_mapping is None # in line 888) - assert nc_data._nc_mapping is None + assert nc_data._nc_mapping is None # pylint: disable=protected-access # Create a simple model for testing model_name = "test_model_gauss_pure_ccl" @@ -567,7 +596,10 @@ def test_numcosmo_gauss_cov_pure_ccl_mode(): model.params_set_default_ftype() # Create a basic cosmology model as well - cosmo = Nc.HICosmoDEXcdm(prim=Nc.HIPrimPowerLaw.new(), reion=Nc.HIReionCamb.new()) + cosmo = Nc.HICosmoDEXcdm( + prim=Nc.HIPrimPowerLaw.new(), # pylint: disable=no-value-for-parameter + reion=Nc.HIReionCamb.new(), # pylint: disable=no-value-for-parameter + ) mset = Ncm.MSet() mset.set(cosmo) diff --git a/tests/connector/numcosmo/test_numcosmo_mapping.py b/tests/connector/numcosmo/test_numcosmo_mapping.py index 0098df044..9e436e942 100644 --- a/tests/connector/numcosmo/test_numcosmo_mapping.py +++ b/tests/connector/numcosmo/test_numcosmo_mapping.py @@ -1,10 +1,11 @@ """Unit tests for the numcosmo Mapping connector.""" -import unittest.mock as mock from typing import cast +from unittest import mock import pyccl as ccl import pytest +import sacc from numcosmo_py import GObject, Nc, Ncm from firecrown.connector.numcosmo import ( @@ -17,9 +18,13 @@ ) from firecrown.likelihood._gaussian import ConstGaussian from firecrown.likelihood._likelihood import Likelihood, NamedParameters +from firecrown.likelihood._statistic import TrivialStatistic from firecrown.modeling_tools import ModelingTools, PoweSpecAmplitudeParameter +from tests._types import NumCosmoCosmologyFixture -Ncm.cfg_init() +# NumCosmo's introspected static functions and enum members confuse Pylint. +# Keep the corresponding suppressions local to the affected calls below. +Ncm.cfg_init() # pylint: disable=no-value-for-parameter @pytest.fixture(name="map_cosmo_dist") @@ -28,19 +33,9 @@ def fixture_map_cosmo_dist() -> MappingNumCosmo: return MappingNumCosmo(dist=Nc.Distance.new(6.0)) -@pytest.fixture(name="map_cosmo_spec_nl") -def fixture_map_cosmo_spec_nl(numcosmo_cosmo) -> MappingNumCosmo: - """Return a MappingNumCosmo instance with the specified p_ml, p_mnl, and dist.""" - return MappingNumCosmo( - p_ml=numcosmo_cosmo["p_ml"], - p_mnl=numcosmo_cosmo["p_mnl"], - dist=numcosmo_cosmo["dist"], - ) - - def test_create_params_map_non_existing_model( map_cosmo_dist: MappingNumCosmo, -): +) -> None: """Test the NumCosmo mapping connector create_params_map with an non existing type.""" @@ -58,7 +53,7 @@ def test_create_params_map_non_existing_model( def test_numcosmo_mapping_create_params_map_absent_model( map_cosmo_dist: MappingNumCosmo, -): +) -> None: """Test the NumCosmo mapping connector create_params_map with an existing type but not present in the model set.""" @@ -76,7 +71,7 @@ def test_numcosmo_mapping_create_params_map_absent_model( def test_numcosmo_mapping_create_params_map_two_models_sharing_parameters( map_cosmo_dist: MappingNumCosmo, -): +) -> None: """Test the NumCosmo mapping connector create_params_map with an existing type but not present in the model set.""" @@ -128,7 +123,7 @@ def test_numcosmo_mapping_create_params_map_two_models_sharing_parameters( fit-type: 0 """ - ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) + ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) # pylint: disable=no-member mb_model1: Ncm.ModelBuilder = cast( Ncm.ModelBuilder, ser.from_yaml(my_model1_dc_yaml) # pylint: disable=no-member ) @@ -159,7 +154,7 @@ def test_numcosmo_mapping_create_params_map_two_models_sharing_parameters( create_params_map(["MyModel1", "MyModel2"], mset, map_cosmo_dist.mapping) -def test_numcosmo_mapping_unsupported(map_cosmo_dist: MappingNumCosmo): +def test_numcosmo_mapping_unsupported(map_cosmo_dist: MappingNumCosmo) -> None: """Test the NumCosmo mapping connector with an unsupported model.""" cosmo = Nc.HICosmoDEJbp() @@ -171,7 +166,7 @@ def test_numcosmo_mapping_unsupported(map_cosmo_dist: MappingNumCosmo): map_cosmo_dist.set_params_from_numcosmo(mset, PoweSpecAmplitudeParameter.SIGMA8) -def test_numcosmo_mapping_missing_hiprim(map_cosmo_dist: MappingNumCosmo): +def test_numcosmo_mapping_missing_hiprim(map_cosmo_dist: MappingNumCosmo) -> None: """Test the NumCosmo mapping connector with a model missing hiprim.""" cosmo = Nc.HICosmoDECpl() @@ -185,7 +180,7 @@ def test_numcosmo_mapping_missing_hiprim(map_cosmo_dist: MappingNumCosmo): map_cosmo_dist.set_params_from_numcosmo(mset, PoweSpecAmplitudeParameter.AS) -def test_numcosmo_mapping_invalid_hiprim(map_cosmo_dist: MappingNumCosmo): +def test_numcosmo_mapping_invalid_hiprim(map_cosmo_dist: MappingNumCosmo) -> None: """Test the NumCosmo mapping connector with a model an invalid hiprim.""" prim = Nc.HIPrimAtan() @@ -204,9 +199,15 @@ def test_numcosmo_mapping_invalid_hiprim(map_cosmo_dist: MappingNumCosmo): "numcosmo_cosmo_fixture", ["numcosmo_cosmo_xcdm", "numcosmo_cosmo_xcdm_no_nu", "numcosmo_cosmo_cpl"], ) -def test_numcosmo_mapping(numcosmo_cosmo_fixture, request): +def test_numcosmo_mapping( + numcosmo_cosmo_fixture: str, + request: pytest.FixtureRequest, +) -> None: """Test the NumCosmo mapping connector consistence.""" - numcosmo_cosmo = request.getfixturevalue(numcosmo_cosmo_fixture) + # Cast: parametrization selects only cosmology fixtures with this shared shape. + numcosmo_cosmo = cast( + NumCosmoCosmologyFixture, request.getfixturevalue(numcosmo_cosmo_fixture) + ) cosmo = numcosmo_cosmo["cosmo"] map_cosmo = MappingNumCosmo( @@ -231,9 +232,15 @@ def test_numcosmo_mapping(numcosmo_cosmo_fixture, request): "numcosmo_cosmo_fixture", ["numcosmo_cosmo_xcdm", "numcosmo_cosmo_xcdm_no_nu", "numcosmo_cosmo_cpl"], ) -def test_numcosmo_serialize_mapping(numcosmo_cosmo_fixture, request): +def test_numcosmo_serialize_mapping( + numcosmo_cosmo_fixture: str, + request: pytest.FixtureRequest, +) -> None: """Test the NumCosmo mapping connector consistence.""" - numcosmo_cosmo = request.getfixturevalue(numcosmo_cosmo_fixture) + # Cast: parametrization selects only cosmology fixtures with this shared shape. + numcosmo_cosmo = cast( + NumCosmoCosmologyFixture, request.getfixturevalue(numcosmo_cosmo_fixture) + ) cosmo = numcosmo_cosmo["cosmo"] map_cosmo = MappingNumCosmo( @@ -244,7 +251,7 @@ def test_numcosmo_serialize_mapping(numcosmo_cosmo_fixture, request): assert map_cosmo.mapping_name == "default" - ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) + ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) # pylint: disable=no-member map_cosmo_dup = ser.dup_obj(map_cosmo) # pylint: disable=no-member assert isinstance(map_cosmo_dup, MappingNumCosmo) @@ -277,15 +284,18 @@ def test_numcosmo_serialize_mapping(numcosmo_cosmo_fixture, request): ["numcosmo_cosmo_xcdm", "numcosmo_cosmo_xcdm_no_nu", "numcosmo_cosmo_cpl"], ) def test_numcosmo_data( - numcosmo_cosmo_fixture, - trivial_stats, - sacc_data_for_trivial_stat, - nc_model_trivial, - request, -): + numcosmo_cosmo_fixture: str, + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + nc_model_trivial: type[Ncm.Model], + request: pytest.FixtureRequest, +) -> None: """Test the NumCosmo data connector for NcmData.""" - numcosmo_cosmo = request.getfixturevalue(numcosmo_cosmo_fixture) + # Cast: parametrization selects only cosmology fixtures with this shared shape. + numcosmo_cosmo = cast( + NumCosmoCosmologyFixture, request.getfixturevalue(numcosmo_cosmo_fixture) + ) cosmo = numcosmo_cosmo["cosmo"] map_cosmo = MappingNumCosmo( @@ -328,15 +338,18 @@ def test_numcosmo_data( ["numcosmo_cosmo_xcdm", "numcosmo_cosmo_xcdm_no_nu", "numcosmo_cosmo_cpl"], ) def test_numcosmo_gauss_cov( - numcosmo_cosmo_fixture, - trivial_stats, - sacc_data_for_trivial_stat, - nc_model_trivial, - request, -): + numcosmo_cosmo_fixture: str, + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + nc_model_trivial: type[Ncm.Model], + request: pytest.FixtureRequest, +) -> None: """Test the NumCosmo data connector for NcmDataGaussCov.""" - numcosmo_cosmo = request.getfixturevalue(numcosmo_cosmo_fixture) + # Cast: parametrization selects only cosmology fixtures with this shared shape. + numcosmo_cosmo = cast( + NumCosmoCosmologyFixture, request.getfixturevalue(numcosmo_cosmo_fixture) + ) cosmo = numcosmo_cosmo["cosmo"] map_cosmo = MappingNumCosmo( @@ -386,13 +399,16 @@ def test_numcosmo_gauss_cov( ], ) def test_numcosmo_serialize_likelihood( - numcosmo_cosmo_fixture, - likelihood_file, - request, -): + numcosmo_cosmo_fixture: str, + likelihood_file: str, + request: pytest.FixtureRequest, +) -> None: """Test the NumCosmo data connector for NcmData with serialization.""" - numcosmo_cosmo = request.getfixturevalue(numcosmo_cosmo_fixture) + # Cast: parametrization selects only cosmology fixtures with this shared shape. + numcosmo_cosmo = cast( + NumCosmoCosmologyFixture, request.getfixturevalue(numcosmo_cosmo_fixture) + ) map_cosmo = MappingNumCosmo( p_ml=numcosmo_cosmo["p_ml"], @@ -413,7 +429,7 @@ def test_numcosmo_serialize_likelihood( assert fc_data.get_length() > 0 assert fc_data.get_dof() > 0 - ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) + ser = Ncm.Serialize.new(Ncm.SerializeOpt.NONE) # pylint: disable=no-member fc_data_dup = ser.dup_obj(fc_data) # pylint: disable=no-member assert isinstance(fc_data_dup, (NumCosmoData, NumCosmoGaussCov)) @@ -435,14 +451,16 @@ def test_numcosmo_serialize_likelihood( assert fc_data_dup.model_list == fc_data.model_list -def test_numcosmo_mapping_deprecated_require_nonlinear_pk(): +def test_numcosmo_mapping_deprecated_require_nonlinear_pk() -> None: """Test the MappingNumCosmo deprecated require_nonlinear_pk.""" with pytest.deprecated_call(): _ = MappingNumCosmo(require_nonlinear_pk=True) -def test_numcosmo_mapping_sigma8_missing_pk(map_cosmo_dist: MappingNumCosmo): +def test_numcosmo_mapping_sigma8_missing_pk( + map_cosmo_dist: MappingNumCosmo, +) -> None: """Test the MappingNumCosmo with sigma8 as a parameter but missing pk.""" cosmo = Nc.HICosmoDEXcdm() @@ -457,12 +475,14 @@ def test_numcosmo_mapping_sigma8_missing_pk(map_cosmo_dist: MappingNumCosmo): @pytest.fixture(name="numcosmo_cosmo") -def fixture_numcosmo_cosmo(): +def fixture_numcosmo_cosmo() -> mock.Mock: """Create a mock NumCosmo cosmology instance.""" return mock.Mock(spec=Nc.HICosmo) -def test_get_amplitude_parameters_sigma8_no_powerspectrum(numcosmo_cosmo): +def test_get_amplitude_parameters_sigma8_no_powerspectrum( + numcosmo_cosmo: mock.Mock, +) -> None: """Test the get_amplitude_parameters function with sigma8 as a parameter but without a power spectrum.""" @@ -474,7 +494,7 @@ def test_get_amplitude_parameters_sigma8_no_powerspectrum(numcosmo_cosmo): ) -def test_accessors_with_no_powerspectrum(): +def test_accessors_with_no_powerspectrum() -> None: mapping = MappingNumCosmo() assert mapping.p_ml is None assert mapping.p_mnl is None @@ -483,21 +503,27 @@ def test_accessors_with_no_powerspectrum(): mapping.p_mnl = None assert mapping.p_mnl is None - linear = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + linear = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) nonlinear = Nc.PowspecMNLHaloFit.new(linear, 3.0, 1.0e-5) mapping.p_mnl = nonlinear assert mapping._p is None # pylint: disable=protected-access -def test_accessors_with_only_linear_powerspectrum(): - linear = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) +def test_accessors_with_only_linear_powerspectrum() -> None: + linear = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) mapping = MappingNumCosmo(p_ml=linear) assert mapping._p is not None # pylint: disable=protected-access mapping.p_ml = linear assert mapping.p_mnl is None # Change to a different linear power spectrum - other_linear = Nc.PowspecMLTransfer.new(Nc.TransferFuncBBKS.new()) + other_linear = Nc.PowspecMLTransfer.new( + Nc.TransferFuncBBKS.new() # pylint: disable=no-value-for-parameter + ) mapping.p_ml = other_linear assert mapping._p is not None # pylint: disable=protected-access assert mapping.p_ml == other_linear @@ -511,13 +537,16 @@ def test_accessors_with_only_linear_powerspectrum(): assert mapping.p_mnl == nonlinear -def test_mapping_ccl_args_bg_only(): +def test_mapping_ccl_args_bg_only() -> None: """Test the MappingNumCosmo class with background only.""" mapping = MappingNumCosmo(dist=Nc.Distance.new(6.0)) assert mapping.p_ml is None assert mapping.p_mnl is None - cosmo = Nc.HICosmoDEXcdm(prim=Nc.HIPrimPowerLaw.new(), reion=Nc.HIReionCamb.new()) + cosmo = Nc.HICosmoDEXcdm( + prim=Nc.HIPrimPowerLaw.new(), # pylint: disable=no-value-for-parameter + reion=Nc.HIReionCamb.new(), # pylint: disable=no-value-for-parameter + ) mset = Ncm.MSet.new_array([cosmo]) mset.prepare_fparam_map() @@ -529,15 +558,20 @@ def test_mapping_ccl_args_bg_only(): assert "pk_nonlin" not in ccl_args -def test_mapping_ccl_args_bg_pk_ml(): +def test_mapping_ccl_args_bg_pk_ml() -> None: """Test the MappingNumCosmo class with background and pk_ml.""" mapping = MappingNumCosmo( dist=Nc.Distance.new(6.0), - p_ml=Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()), + p_ml=Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ), ) assert mapping.p_mnl is None - cosmo = Nc.HICosmoDEXcdm(prim=Nc.HIPrimPowerLaw.new(), reion=Nc.HIReionCamb.new()) + cosmo = Nc.HICosmoDEXcdm( + prim=Nc.HIPrimPowerLaw.new(), # pylint: disable=no-value-for-parameter + reion=Nc.HIReionCamb.new(), # pylint: disable=no-value-for-parameter + ) mset = Ncm.MSet.new_array([cosmo]) mset.prepare_fparam_map() @@ -549,9 +583,11 @@ def test_mapping_ccl_args_bg_pk_ml(): assert "pk_nonlin" not in ccl_args -def test_mapping_ccl_args_bg_pk_ml_pk_mnl(): +def test_mapping_ccl_args_bg_pk_ml_pk_mnl() -> None: """Test the MappingNumCosmo class with background, pk_ml and pk_mnl.""" - p_ml = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + p_ml = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) mapping = MappingNumCosmo( dist=Nc.Distance.new(6.0), p_ml=p_ml, @@ -560,7 +596,10 @@ def test_mapping_ccl_args_bg_pk_ml_pk_mnl(): assert mapping.p_ml is not None assert mapping.p_mnl is not None - cosmo = Nc.HICosmoDEXcdm(prim=Nc.HIPrimPowerLaw.new(), reion=Nc.HIReionCamb.new()) + cosmo = Nc.HICosmoDEXcdm( + prim=Nc.HIPrimPowerLaw.new(), # pylint: disable=no-value-for-parameter + reion=Nc.HIReionCamb.new(), # pylint: disable=no-value-for-parameter + ) mset = Ncm.MSet.new_array([cosmo]) mset.prepare_fparam_map() @@ -572,9 +611,11 @@ def test_mapping_ccl_args_bg_pk_ml_pk_mnl(): assert "pk_nonlin" in ccl_args -def test_mapping_ccl_args_bg_pk_mnl(): +def test_mapping_ccl_args_bg_pk_mnl() -> None: """Test the MappingNumCosmo class with background, pk_ml and pk_mnl.""" - p_ml = Nc.PowspecMLTransfer.new(Nc.TransferFuncEH.new()) + p_ml = Nc.PowspecMLTransfer.new( + Nc.TransferFuncEH.new() # pylint: disable=no-value-for-parameter + ) with pytest.raises( AssertionError, match="PowspecML object must be provided when using PowspecMNL" ): diff --git a/tests/connector/test_mapping.py b/tests/connector/test_mapping.py index fb94790bb..ce8a05478 100644 --- a/tests/connector/test_mapping.py +++ b/tests/connector/test_mapping.py @@ -20,7 +20,7 @@ # TODO: Refactor these test functions to use a sensible fixture. -def test_conversion_from_cosmosis_camb_as(): +def test_conversion_from_cosmosis_camb_as() -> None: cosmosis_params = { "a_s": 2.1e-09, "baryon_fraction": 0.13333333333333333, @@ -65,7 +65,7 @@ def test_conversion_from_cosmosis_camb_as(): assert p.T_CMB == 2.7255 # currently hard-wired -def test_conversion_from_cosmosis_camb_sigma8(): +def test_conversion_from_cosmosis_camb_sigma8() -> None: cosmosis_params = { "a_s": 2.1e-09, "baryon_fraction": 0.13333333333333333, @@ -111,7 +111,7 @@ def test_conversion_from_cosmosis_camb_sigma8(): assert p.T_CMB == 2.7255 # currently hard-wired -def test_conversion_from_cosmosis_camb_using_delta_neff(): +def test_conversion_from_cosmosis_camb_using_delta_neff() -> None: cosmosis_params = { "a_s": 2.1e-09, "baryon_fraction": 0.13333333333333333, @@ -147,7 +147,7 @@ def test_conversion_from_cosmosis_camb_using_delta_neff(): assert p.Neff == pytest.approx(3.171) -def test_get_params_names(): +def test_get_params_names() -> None: fc_map = Mapping() with pytest.deprecated_call(): @@ -155,7 +155,7 @@ def test_get_params_names(): assert not params_names -def test_transform_k_h_to_k(): +def test_transform_k_h_to_k() -> None: fc_map = Mapping() with pytest.deprecated_call(): @@ -163,7 +163,7 @@ def test_transform_k_h_to_k(): fc_map.transform_k_h_to_k(np.array([], dtype=np.float64)) -def test_transform_p_k_h3_to_p_k(): +def test_transform_p_k_h3_to_p_k() -> None: fc_map = Mapping() with pytest.deprecated_call(): @@ -171,7 +171,7 @@ def test_transform_p_k_h3_to_p_k(): fc_map.transform_p_k_h3_to_p_k(np.array([], dtype=np.float64)) -def test_transform_h_to_h_over_h0(): +def test_transform_h_to_h_over_h0() -> None: fc_map = Mapping() with pytest.deprecated_call(): @@ -179,7 +179,7 @@ def test_transform_h_to_h_over_h0(): fc_map.transform_h_to_h_over_h0(np.array([], dtype=np.float64)) -def test_sigma8_and_A_s(): +def test_sigma8_and_A_s() -> None: fc_map = Mapping() params_dict: dict[str, Any] = { @@ -210,14 +210,14 @@ def test_sigma8_and_A_s(): fc_map.set_params(**params_dict) -def test_mappping_builder(): +def test_mappping_builder() -> None: with pytest.raises( ValueError, match="input_style must be .* not invalid_input_style" ): mapping_builder(input_style="invalid_input_style") -def test_mapping_cosmosis(): +def test_mapping_cosmosis() -> None: mapping_cosmosis = mapping_builder(input_style="CosmoSIS") assert isinstance(mapping_cosmosis, Mapping) @@ -268,21 +268,21 @@ def test_mapping_cosmosis(): assert d["m_nu"] == 0.0 -def test_mapping_cosmosis_k_h_to_h(mapping_cosmosis): +def test_mapping_cosmosis_k_h_to_h(mapping_cosmosis: MappingCosmoSIS) -> None: k_h_array = np.geomspace(0.1, 10.0, 10) k_array = mapping_cosmosis.transform_k_h_to_k(k_h_array) assert np.allclose(k_h_array * mapping_cosmosis.h, k_array) -def test_mapping_cosmosis_p_k_h3_to_p_k(mapping_cosmosis): +def test_mapping_cosmosis_p_k_h3_to_p_k(mapping_cosmosis: MappingCosmoSIS) -> None: p_k_h3_array = np.geomspace(0.1, 10.0, 10) p_k_array = mapping_cosmosis.transform_p_k_h3_to_p_k(p_k_h3_array) assert np.allclose(p_k_h3_array / mapping_cosmosis.h**3, p_k_array) -def test_mapping_camb(): +def test_mapping_camb() -> None: mapping_camb = mapping_builder(input_style="CAMB") assert isinstance(mapping_camb, MappingCAMB) @@ -333,7 +333,7 @@ def test_mapping_camb(): assert d["m_nu"] == 0.0 -def test_mapping_camb_setting_both_A_s_and_sigma8(): +def test_mapping_camb_setting_both_A_s_and_sigma8() -> None: mapping_camb = mapping_builder(input_style="CAMB") assert isinstance(mapping_camb, MappingCAMB) with pytest.raises( @@ -358,7 +358,7 @@ def test_mapping_camb_setting_both_A_s_and_sigma8(): ) -def test_mapping_camb_setting_neither_A_s_and_sigma8(): +def test_mapping_camb_setting_neither_A_s_and_sigma8() -> None: mapping_camb = mapping_builder(input_style="CAMB") assert isinstance(mapping_camb, MappingCAMB) with pytest.raises( @@ -381,7 +381,7 @@ def test_mapping_camb_setting_neither_A_s_and_sigma8(): ) -def test_mapping_camb_setting_A_s(): +def test_mapping_camb_setting_A_s() -> None: mapping_camb = mapping_builder(input_style="CAMB") assert isinstance(mapping_camb, MappingCAMB) mapping_camb.set_params_from_camb( @@ -403,7 +403,7 @@ def test_mapping_camb_setting_A_s(): assert mapping_camb.A_s == 2.1e-9 -def test_mapping_camb_setting_sigma8(): +def test_mapping_camb_setting_sigma8() -> None: mapping_camb = mapping_builder(input_style="CAMB") assert isinstance(mapping_camb, MappingCAMB) mapping_camb.set_params_from_camb( @@ -425,7 +425,7 @@ def test_mapping_camb_setting_sigma8(): assert mapping_camb.sigma8 == 0.8 -def test_mapping_m_nu_list_asdict(): +def test_mapping_m_nu_list_asdict() -> None: mapping0 = Mapping() mapping0.Omega_c = 0.26 mapping0.Omega_b = 0.04 diff --git a/tests/example/test_cluster_number_counts.py b/tests/example/test_cluster_number_counts.py index f6a62d7ad..8c6caa99e 100644 --- a/tests/example/test_cluster_number_counts.py +++ b/tests/example/test_cluster_number_counts.py @@ -28,14 +28,14 @@ @pytest.fixture(name="target_framework", params=Frameworks) -def fixture_target_framework(request) -> Frameworks: +def fixture_target_framework(request: pytest.FixtureRequest) -> Frameworks: """Generate a target framework for all frameworks.""" return request.param @pytest.fixture(name="cluster_example", params=CLUSTER_CASES) def fixture_cluster_example( - request, target_framework: Frameworks, tmp_path: Path + request: pytest.FixtureRequest, target_framework: Frameworks, tmp_path: Path ) -> tuple[Path, Frameworks]: """Generate each cluster example for all frameworks.""" example_cls, options = CLUSTER_CASES[request.param] diff --git a/tests/example/test_cmb_cross.py b/tests/example/test_cmb_cross.py index 07b86a9bc..f90fb58f3 100644 --- a/tests/example/test_cmb_cross.py +++ b/tests/example/test_cmb_cross.py @@ -10,7 +10,7 @@ @pytest.fixture(name="target_framework", params=Frameworks) -def fixture_target_framework(request) -> Frameworks: +def fixture_target_framework(request: pytest.FixtureRequest) -> Frameworks: """Generate a target framework for all frameworks.""" return request.param @@ -33,7 +33,7 @@ def fixture_cmb_cross_example( @pytest.mark.example -def test_cmb_cross_run(cmb_cross_example): +def test_cmb_cross_run(cmb_cross_example: tuple[Path, Frameworks]) -> None: """Run each framework's generated configuration end-to-end. This is what actually exercises the background/distance splines out to diff --git a/tests/example/test_cosmic_shear.py b/tests/example/test_cosmic_shear.py index 089ee96ad..43f9f6106 100644 --- a/tests/example/test_cosmic_shear.py +++ b/tests/example/test_cosmic_shear.py @@ -10,13 +10,13 @@ @pytest.fixture(name="target_framework", params=Frameworks) -def fixture_target_framework(request) -> Frameworks: +def fixture_target_framework(request: pytest.FixtureRequest) -> Frameworks: """Generate a target framework for all frameworks.""" return request.param @pytest.fixture(name="use_absolute_path", params=[True, False]) -def fixture_use_absolute_path(request) -> bool: +def fixture_use_absolute_path(request: pytest.FixtureRequest) -> bool: """Generate a use_absolute_path for all path types.""" return request.param @@ -37,7 +37,7 @@ def fixture_cosmic_shear_example( @pytest.mark.example -def test_cosmic_shear_run(cosmic_shear_example): +def test_cosmic_shear_run(cosmic_shear_example: tuple[Path, Frameworks]) -> None: output_path, framework = cosmic_shear_example match framework: case Frameworks.COSMOSIS: diff --git a/tests/example/test_des_y1_3x2pt.py b/tests/example/test_des_y1_3x2pt.py index b72e5b311..decc953e2 100644 --- a/tests/example/test_des_y1_3x2pt.py +++ b/tests/example/test_des_y1_3x2pt.py @@ -10,19 +10,19 @@ @pytest.fixture(name="target_framework", params=Frameworks) -def fixture_target_framework(request) -> Frameworks: +def fixture_target_framework(request: pytest.FixtureRequest) -> Frameworks: """Generate a target framework for all frameworks.""" return request.param @pytest.fixture(name="use_absolute_path", params=[True, False]) -def fixture_use_absolute_path(request) -> bool: +def fixture_use_absolute_path(request: pytest.FixtureRequest) -> bool: """Generate a use_absolute_path for all path types.""" return request.param @pytest.fixture(name="factory_type", params=DESY1FactoryType) -def fixture_factory_type(request) -> DESY1FactoryType: +def fixture_factory_type(request: pytest.FixtureRequest) -> DESY1FactoryType: """Generate a factory type for all factory types.""" return request.param @@ -51,7 +51,7 @@ def fixture_des_y1_3x2pt_example( @pytest.mark.example -def test_des_y1_3x2pt_run(des_y1_3x2pt_example): +def test_des_y1_3x2pt_run(des_y1_3x2pt_example: tuple[Path, Frameworks]) -> None: output_path, framework = des_y1_3x2pt_example match framework: case Frameworks.COSMOSIS: diff --git a/tests/example/test_srd_sn.py b/tests/example/test_srd_sn.py index 28cd36f81..756e7831e 100644 --- a/tests/example/test_srd_sn.py +++ b/tests/example/test_srd_sn.py @@ -10,13 +10,13 @@ @pytest.fixture(name="target_framework", params=Frameworks) -def fixture_target_framework(request) -> Frameworks: +def fixture_target_framework(request: pytest.FixtureRequest) -> Frameworks: """Generate a target framework for all frameworks.""" return request.param @pytest.fixture(name="use_absolute_path", params=[True, False]) -def fixture_use_absolute_path(request) -> bool: +def fixture_use_absolute_path(request: pytest.FixtureRequest) -> bool: """Generate a use_absolute_path for all path types.""" return request.param @@ -37,7 +37,7 @@ def fixture_sn_srd_example( @pytest.mark.example -def test_srd_sn_run(sn_srd_example): +def test_srd_sn_run(sn_srd_example: tuple[Path, Frameworks]) -> None: output_path, framework = sn_srd_example match framework: case Frameworks.COSMOSIS: diff --git a/tests/fctools/test_ast_utils.py b/tests/fctools/test_ast_utils.py index 41bab6415..f01dcfea9 100644 --- a/tests/fctools/test_ast_utils.py +++ b/tests/fctools/test_ast_utils.py @@ -4,6 +4,7 @@ """ import ast +from pathlib import Path import pytest @@ -22,7 +23,7 @@ class TestGetModuleDocstring: """Tests for get_module_docstring function.""" - def test_get_simple_docstring(self, tmp_path): + def test_get_simple_docstring(self, tmp_path: Path) -> None: """Test extracting a simple module docstring.""" py_file = tmp_path / "test_module.py" py_file.write_text('"""This is a module docstring."""\n\ndef func():\n pass') @@ -31,7 +32,7 @@ def test_get_simple_docstring(self, tmp_path): assert result == "This is a module docstring." - def test_get_multiline_docstring(self, tmp_path): + def test_get_multiline_docstring(self, tmp_path: Path) -> None: """Test extracting a multi-line module docstring.""" py_file = tmp_path / "test_module.py" content = '''"""This is the first line. @@ -50,7 +51,7 @@ def func(): assert "This is the first line." in result assert "This is the second paragraph." in result - def test_get_no_docstring(self, tmp_path): + def test_get_no_docstring(self, tmp_path: Path) -> None: """Test file with no module docstring.""" py_file = tmp_path / "test_module.py" py_file.write_text("def func():\n pass") @@ -59,7 +60,7 @@ def test_get_no_docstring(self, tmp_path): assert result is None - def test_get_single_quote_docstring(self, tmp_path): + def test_get_single_quote_docstring(self, tmp_path: Path) -> None: """Test extracting docstring with single quotes.""" py_file = tmp_path / "test_module.py" py_file.write_text("'''Single quote docstring'''\n\ndef func():\n pass") @@ -68,14 +69,14 @@ def test_get_single_quote_docstring(self, tmp_path): assert result == "Single quote docstring" - def test_nonexistent_file(self, tmp_path): + def test_nonexistent_file(self, tmp_path: Path) -> None: """Test reading from a nonexistent file.""" py_file = tmp_path / "nonexistent.py" with pytest.raises(OSError): get_module_docstring(py_file) - def test_invalid_python_syntax(self, tmp_path): + def test_invalid_python_syntax(self, tmp_path: Path) -> None: """Test file with invalid Python syntax.""" py_file = tmp_path / "invalid.py" py_file.write_text("def broken(\n # missing closing paren") @@ -87,7 +88,7 @@ def test_invalid_python_syntax(self, tmp_path): class TestGetClassDefinition: """Tests for get_class_definition function.""" - def test_find_simple_class(self): + def test_find_simple_class(self) -> None: """Test finding a simple class definition.""" source = """ class MyClass: @@ -99,7 +100,7 @@ class MyClass: assert isinstance(result, ast.ClassDef) assert result.name == "MyClass" - def test_find_class_with_docstring(self): + def test_find_class_with_docstring(self) -> None: """Test finding a class with a docstring.""" source = ''' class DocumentedClass: @@ -113,7 +114,7 @@ class DocumentedClass: docstring = ast.get_docstring(result) assert docstring == "This is a documented class." - def test_find_nested_class(self): + def test_find_nested_class(self) -> None: """Test finding a nested class.""" source = """ class OuterClass: @@ -125,7 +126,7 @@ class InnerClass: assert result is not None assert result.name == "InnerClass" - def test_class_not_found(self): + def test_class_not_found(self) -> None: """Test searching for a nonexistent class.""" source = """ class MyClass: @@ -135,7 +136,7 @@ class MyClass: assert result is None - def test_multiple_classes(self): + def test_multiple_classes(self) -> None: """Test finding the correct class among multiple classes.""" source = """ class FirstClass: @@ -152,7 +153,7 @@ class ThirdClass: assert result is not None assert result.name == "SecondClass" - def test_invalid_syntax(self): + def test_invalid_syntax(self) -> None: """Test parsing source with invalid syntax.""" source = "class BrokenClass" @@ -163,13 +164,13 @@ def test_invalid_syntax(self): class TestFormatDocstringSummary: """Tests for format_docstring_summary function.""" - def test_format_simple_docstring(self): + def test_format_simple_docstring(self) -> None: """Test formatting a simple one-line docstring.""" result = format_docstring_summary("This is a simple docstring.") assert result == "This is a simple docstring." - def test_format_multiline_docstring(self): + def test_format_multiline_docstring(self) -> None: """Test formatting a multi-line docstring (returns first line).""" docstring = """This is the first line. @@ -180,25 +181,25 @@ def test_format_multiline_docstring(self): assert result == "This is the first line." - def test_format_none_docstring(self): + def test_format_none_docstring(self) -> None: """Test formatting None docstring.""" result = format_docstring_summary(None) assert result == "No description available" - def test_format_empty_docstring(self): + def test_format_empty_docstring(self) -> None: """Test formatting an empty docstring.""" result = format_docstring_summary("") assert result == "No description available" - def test_format_whitespace_only_docstring(self): + def test_format_whitespace_only_docstring(self) -> None: """Test formatting a docstring with only whitespace.""" result = format_docstring_summary(" \n\n ") assert result == "No description available" - def test_format_long_docstring_truncation(self): + def test_format_long_docstring_truncation(self) -> None: """Test truncation of long docstrings.""" long_text = "a" * 100 result = format_docstring_summary(long_text, max_length=80) @@ -207,7 +208,7 @@ def test_format_long_docstring_truncation(self): assert result.endswith("...") assert result.startswith("aaa") - def test_format_docstring_with_triple_quotes(self): + def test_format_docstring_with_triple_quotes(self) -> None: """Test docstring that includes triple quotes gets filtered correctly.""" # The function filters out lines starting with triple quotes result = format_docstring_summary('"""This is a docstring."""') @@ -215,7 +216,7 @@ def test_format_docstring_with_triple_quotes(self): # Since the only line starts with triple quotes, it's filtered assert result == "No description available" - def test_format_docstring_preserves_short_text(self): + def test_format_docstring_preserves_short_text(self) -> None: """Test that short docstrings are not truncated.""" short_text = "Short description" result = format_docstring_summary(short_text, max_length=80) @@ -223,7 +224,7 @@ def test_format_docstring_preserves_short_text(self): assert result == short_text assert not result.endswith("...") - def test_format_custom_max_length(self): + def test_format_custom_max_length(self) -> None: """Test custom maximum length parameter.""" text = "This is a moderately long description" result = format_docstring_summary(text, max_length=20) @@ -235,7 +236,7 @@ def test_format_custom_max_length(self): class TestExtractClassAttributes: """Tests for extract_class_attributes function.""" - def test_extract_simple_attributes(self): + def test_extract_simple_attributes(self) -> None: """Test extracting simple class attributes.""" source = """ class MyClass: @@ -251,7 +252,7 @@ class MyClass: assert "attr2" in result assert "attr3" in result - def test_extract_annotated_attributes(self): + def test_extract_annotated_attributes(self) -> None: """Test extracting type-annotated attributes.""" source = """ class MyClass: @@ -267,7 +268,7 @@ class MyClass: assert "age" in result assert "value" in result - def test_extract_no_attributes(self): + def test_extract_no_attributes(self) -> None: """Test class with no attributes.""" source = """ class MyClass: @@ -280,7 +281,7 @@ def method(self): assert not result - def test_extract_mixed_attributes(self): + def test_extract_mixed_attributes(self) -> None: """Test class with both annotated and simple attributes.""" source = """ class MyClass: @@ -296,7 +297,7 @@ class MyClass: assert "annotated" in result assert "no_value" in result - def test_ignores_instance_attributes(self): + def test_ignores_instance_attributes(self) -> None: """Test that instance attributes in __init__ are not extracted.""" source = """ class MyClass: @@ -312,7 +313,7 @@ def __init__(self): assert "class_attr" in result assert "instance_attr" not in result - def test_extract_non_name_targets(self): + def test_extract_non_name_targets(self) -> None: """Test handling of assignments to non-Name targets.""" source = """ class MyClass: @@ -334,7 +335,7 @@ class MyClass: class TestFormatClassDocstring: """Tests for format_class_docstring function.""" - def test_format_simple_docstring(self): + def test_format_simple_docstring(self) -> None: """Test formatting a simple class docstring.""" source = ''' class MyClass: @@ -348,7 +349,7 @@ class MyClass: assert len(result) > 0 assert any("Simple docstring." in line for line in result) - def test_format_multiline_docstring(self): + def test_format_multiline_docstring(self) -> None: """Test formatting a multi-line class docstring.""" source = ''' class MyClass: @@ -366,7 +367,7 @@ class MyClass: joined = " ".join(result) assert "First line." in joined - def test_format_no_docstring(self): + def test_format_no_docstring(self) -> None: """Test formatting when class has no docstring.""" source = """ class MyClass: @@ -378,7 +379,7 @@ class MyClass: assert not result - def test_format_preserves_structure(self): + def test_format_preserves_structure(self) -> None: """Test that docstring formatting preserves basic structure.""" source = ''' class MyClass: @@ -400,7 +401,7 @@ class MyClass: assert "Args:" in joined assert "Returns:" in joined - def test_format_empty_string_docstring(self): + def test_format_empty_string_docstring(self) -> None: """Test formatting when docstring is an empty string.""" # Create a ClassDef node with empty docstring manually # This is an edge case that's hard to create via parsing @@ -420,7 +421,7 @@ def test_format_empty_string_docstring(self): class TestGetFunctionNames: """Tests for get_function_names function.""" - def test_get_simple_methods(self): + def test_get_simple_methods(self) -> None: """Test extracting method names from a simple class.""" source = """ class MyClass: @@ -438,7 +439,7 @@ def method2(self): assert "method2" in result assert len(result) == 2 - def test_get_no_methods(self): + def test_get_no_methods(self) -> None: """Test class with no methods.""" source = """ class MyClass: @@ -450,7 +451,7 @@ class MyClass: assert not result - def test_get_special_methods(self): + def test_get_special_methods(self) -> None: """Test extracting special methods like __init__.""" source = """ class MyClass: @@ -471,7 +472,7 @@ def regular_method(self): assert "__str__" in result assert "regular_method" in result - def test_get_static_and_class_methods(self): + def test_get_static_and_class_methods(self) -> None: """Test extracting static and class methods.""" source = """ class MyClass: @@ -494,7 +495,7 @@ def instance_method(self): assert "class_method" in result assert "instance_method" in result - def test_ignores_nested_functions(self): + def test_ignores_nested_functions(self) -> None: """Test that nested functions are not included.""" source = """ class MyClass: diff --git a/tests/fctools/test_code_block_checker.py b/tests/fctools/test_code_block_checker.py index 44c1b187b..29374fd4d 100644 --- a/tests/fctools/test_code_block_checker.py +++ b/tests/fctools/test_code_block_checker.py @@ -33,7 +33,7 @@ def create_temp_qmd_file(content: str) -> Path: # Tests for check_qmd_file() -def test_check_qmd_file_no_code_blocks(): +def test_check_qmd_file_no_code_blocks() -> None: """Test with a QMD file containing no code blocks.""" content = """# Tutorial Title @@ -49,7 +49,7 @@ def test_check_qmd_file_no_code_blocks(): qmd_file.unlink() -def test_check_qmd_file_valid_python_block(): +def test_check_qmd_file_valid_python_block() -> None: """Test with valid Python code block.""" content = """# Tutorial @@ -71,7 +71,7 @@ def test_check_qmd_file_valid_python_block(): qmd_file.unlink() -def test_check_qmd_file_invalid_python_block(): +def test_check_qmd_file_invalid_python_block() -> None: """Test with invalid Python code block.""" content = """# Tutorial @@ -93,7 +93,7 @@ def test_check_qmd_file_invalid_python_block(): qmd_file.unlink() -def test_check_qmd_file_multiple_valid_blocks(): +def test_check_qmd_file_multiple_valid_blocks() -> None: """Test with multiple valid Python blocks.""" content = """# Tutorial @@ -125,7 +125,7 @@ def foo(): qmd_file.unlink() -def test_check_qmd_file_multiple_blocks_with_errors(): +def test_check_qmd_file_multiple_blocks_with_errors() -> None: """Test with multiple Python blocks where some have errors.""" content = """# Tutorial @@ -164,7 +164,7 @@ def foo( qmd_file.unlink() -def test_check_qmd_file_non_python_code_blocks(): +def test_check_qmd_file_non_python_code_blocks() -> None: """Test that non-Python code blocks are ignored.""" content = """# Tutorial @@ -197,7 +197,7 @@ def test_check_qmd_file_non_python_code_blocks(): qmd_file.unlink() -def test_check_qmd_file_python_block_with_options(): +def test_check_qmd_file_python_block_with_options() -> None: """Test Python block with execution options.""" content = """# Tutorial @@ -222,7 +222,7 @@ def test_check_qmd_file_python_block_with_options(): qmd_file.unlink() -def test_check_qmd_file_empty_python_block(): +def test_check_qmd_file_empty_python_block() -> None: """Test with empty Python code block.""" content = """# Tutorial @@ -241,7 +241,7 @@ def test_check_qmd_file_empty_python_block(): qmd_file.unlink() -def test_check_qmd_file_python_block_with_comments(): +def test_check_qmd_file_python_block_with_comments() -> None: """Test Python block with comments.""" content = """# Tutorial @@ -260,7 +260,7 @@ def test_check_qmd_file_python_block_with_comments(): qmd_file.unlink() -def test_check_qmd_file_display_only_python_block(): +def test_check_qmd_file_display_only_python_block() -> None: """Test display-only Python block with .python format.""" content = """# Tutorial @@ -279,7 +279,7 @@ def test_check_qmd_file_display_only_python_block(): qmd_file.unlink() -def test_check_qmd_file_display_only_with_syntax_error(): +def test_check_qmd_file_display_only_with_syntax_error() -> None: """Test display-only Python block with syntax error.""" content = """# Tutorial @@ -301,7 +301,7 @@ def test_check_qmd_file_display_only_with_syntax_error(): qmd_file.unlink() -def test_check_qmd_file_mixed_executable_and_display(): +def test_check_qmd_file_mixed_executable_and_display() -> None: """Test mix of executable and display-only blocks.""" content = """# Tutorial @@ -332,7 +332,7 @@ def test_check_qmd_file_mixed_executable_and_display(): qmd_file.unlink() -def test_check_qmd_file_syntax_error_details(): +def test_check_qmd_file_syntax_error_details() -> None: """Test that syntax error details are captured correctly.""" content = """# Tutorial @@ -353,7 +353,7 @@ def test_check_qmd_file_syntax_error_details(): qmd_file.unlink() -def test_check_qmd_file_multiline_statements(): +def test_check_qmd_file_multiline_statements() -> None: """Test Python block with multiline statements.""" content = """# Tutorial @@ -382,7 +382,7 @@ def long_function( # Tests for main() -def test_main_with_valid_directory(): +def test_main_with_valid_directory() -> None: """Test main function with a directory containing valid QMD files.""" # Create temporary directory with valid QMD file with tempfile.TemporaryDirectory() as temp_dir: @@ -403,14 +403,14 @@ def test_main_with_valid_directory(): assert "No syntax errors" in result.stderr -def test_main_with_invalid_directory(): +def test_main_with_invalid_directory() -> None: """Test main function with non-existent directory.""" result = runner.invoke(app, ["/nonexistent/directory"]) assert result.exit_code != 0 -def test_main_with_syntax_errors(): +def test_main_with_syntax_errors() -> None: """Test main function with QMD files containing syntax errors.""" with tempfile.TemporaryDirectory() as temp_dir: qmd_path = Path(temp_dir) / "test.qmd" @@ -430,7 +430,7 @@ def test_main_with_syntax_errors(): assert "Block 1" in result.stderr -def test_main_with_multiple_files(): +def test_main_with_multiple_files() -> None: """Test main function with multiple QMD files.""" with tempfile.TemporaryDirectory() as temp_dir: # Valid file @@ -456,7 +456,7 @@ def test_main_with_multiple_files(): assert "Total syntax errors" in result.stderr -def test_main_with_empty_directory(): +def test_main_with_empty_directory() -> None: """Test main function with empty directory.""" with tempfile.TemporaryDirectory() as temp_dir: result = runner.invoke(app, [temp_dir]) @@ -466,7 +466,7 @@ def test_main_with_empty_directory(): assert "No syntax errors" in result.stderr -def test_main_quiet_mode_no_errors(): +def test_main_quiet_mode_no_errors() -> None: """Test --quiet flag suppresses success messages.""" with tempfile.TemporaryDirectory() as temp_dir: qmd_path = Path(temp_dir) / "valid.qmd" @@ -484,7 +484,7 @@ def test_main_quiet_mode_no_errors(): assert result.stderr.strip() == "" -def test_main_quiet_mode_with_errors(): +def test_main_quiet_mode_with_errors() -> None: """Test --quiet flag still reports errors.""" with tempfile.TemporaryDirectory() as temp_dir: qmd_path = Path(temp_dir) / "error.qmd" diff --git a/tests/fctools/test_common.py b/tests/fctools/test_common.py index d0be99c72..4b0c7676b 100644 --- a/tests/fctools/test_common.py +++ b/tests/fctools/test_common.py @@ -23,7 +23,7 @@ @pytest.fixture -def console(): +def console() -> Console: """Return a rich console object for testing.""" return Console() @@ -31,7 +31,7 @@ def console(): class TestLoadJsonFile: """Tests for load_json_file function.""" - def test_load_valid_json(self, tmp_path, console): + def test_load_valid_json(self, tmp_path: Path, console: Console) -> None: """Test loading a valid JSON file.""" json_file = tmp_path / "test.json" test_data = {"key": "value", "number": 42, "list": [1, 2, 3]} @@ -41,7 +41,9 @@ def test_load_valid_json(self, tmp_path, console): assert result == test_data - def test_load_json_with_custom_context(self, tmp_path, console): + def test_load_json_with_custom_context( + self, tmp_path: Path, console: Console + ) -> None: """Test that custom error context is used in error messages.""" json_file = tmp_path / "test.json" json_file.write_text("{invalid json}") @@ -51,7 +53,7 @@ def test_load_json_with_custom_context(self, tmp_path, console): assert exc_info.value.code == 1 - def test_load_nonexistent_file(self, tmp_path, console): + def test_load_nonexistent_file(self, tmp_path: Path, console: Console) -> None: """Test loading a file that doesn't exist.""" json_file = tmp_path / "nonexistent.json" @@ -60,7 +62,7 @@ def test_load_nonexistent_file(self, tmp_path, console): assert exc_info.value.code == 1 - def test_load_invalid_json(self, tmp_path, console): + def test_load_invalid_json(self, tmp_path: Path, console: Console) -> None: """Test loading a file with invalid JSON.""" json_file = tmp_path / "invalid.json" json_file.write_text("{this is not valid JSON}") @@ -70,7 +72,7 @@ def test_load_invalid_json(self, tmp_path, console): assert exc_info.value.code == 1 - def test_load_empty_file(self, tmp_path, console): + def test_load_empty_file(self, tmp_path: Path, console: Console) -> None: """Test loading an empty file.""" json_file = tmp_path / "empty.json" json_file.write_text("") @@ -80,7 +82,7 @@ def test_load_empty_file(self, tmp_path, console): assert exc_info.value.code == 1 - def test_load_complex_json(self, tmp_path, console): + def test_load_complex_json(self, tmp_path: Path, console: Console) -> None: """Test loading JSON with nested structures.""" json_file = tmp_path / "complex.json" test_data = { @@ -98,14 +100,14 @@ def test_load_complex_json(self, tmp_path, console): class TestImportClassFromPath: """Tests for import_class_from_path function.""" - def test_import_builtin_class(self, console): + def test_import_builtin_class(self, console: Console) -> None: """Test importing a built-in Python class.""" result = import_class_from_path(console, "pathlib.Path") assert result == Path assert isinstance(Path(), result) - def test_import_nested_class(self, console): + def test_import_nested_class(self, console: Console) -> None: """Test importing a class from a nested module.""" result = import_class_from_path(console, "collections.abc.Mapping") @@ -113,28 +115,28 @@ def test_import_nested_class(self, console): assert result == Mapping - def test_import_invalid_module(self, console): + def test_import_invalid_module(self, console: Console) -> None: """Test importing from a nonexistent module.""" with pytest.raises(SystemExit) as exc_info: import_class_from_path(console, "nonexistent.module.Class") assert exc_info.value.code == 1 - def test_import_invalid_class(self, console): + def test_import_invalid_class(self, console: Console) -> None: """Test importing a nonexistent class from a valid module.""" with pytest.raises(SystemExit) as exc_info: import_class_from_path(console, "pathlib.NonexistentClass") assert exc_info.value.code == 1 - def test_import_invalid_path_format(self, console): + def test_import_invalid_path_format(self, console: Console) -> None: """Test importing with an invalid path format (no dots).""" with pytest.raises(SystemExit) as exc_info: import_class_from_path(console, "InvalidPath") assert exc_info.value.code == 1 - def test_import_firecrown_class(self, console): + def test_import_firecrown_class(self, console: Console) -> None: """Test importing a class from firecrown.""" result = import_class_from_path(console, "firecrown.updatable.Updatable") @@ -147,7 +149,7 @@ def test_import_firecrown_class(self, console): class TestImportModuleFromFile: """Tests for import_module_from_file function.""" - def test_import_valid_module(self, tmp_path, console): + def test_import_valid_module(self, tmp_path: Path, console: Console) -> None: """Test importing a valid Python module from a file.""" module_file = tmp_path / "test_module.py" module_file.write_text(""" @@ -163,7 +165,9 @@ def test_function(): assert module.test_function() == 42 assert module.TEST_CONSTANT == "hello" - def test_import_module_with_custom_name(self, tmp_path, console): + def test_import_module_with_custom_name( + self, tmp_path: Path, console: Console + ) -> None: """Test importing a module with a custom name.""" module_file = tmp_path / "custom.py" module_file.write_text("VALUE = 123") @@ -172,7 +176,7 @@ def test_import_module_with_custom_name(self, tmp_path, console): assert module.VALUE == 123 - def test_import_nonexistent_file(self, tmp_path, console): + def test_import_nonexistent_file(self, tmp_path: Path, console: Console) -> None: """Test importing from a nonexistent file.""" module_file = tmp_path / "nonexistent.py" @@ -181,7 +185,9 @@ def test_import_nonexistent_file(self, tmp_path, console): assert exc_info.value.code == 1 - def test_import_file_with_syntax_error(self, tmp_path, console): + def test_import_file_with_syntax_error( + self, tmp_path: Path, console: Console + ) -> None: """Test importing a file with syntax errors.""" module_file = tmp_path / "syntax_error.py" module_file.write_text("def broken_function(\n # missing closing paren") @@ -191,7 +197,9 @@ def test_import_file_with_syntax_error(self, tmp_path, console): assert exc_info.value.code == 1 - def test_import_file_with_import_error(self, tmp_path, console): + def test_import_file_with_import_error( + self, tmp_path: Path, console: Console + ) -> None: """Test importing a file that imports a nonexistent module.""" module_file = tmp_path / "import_error.py" module_file.write_text("import nonexistent_module") @@ -201,7 +209,7 @@ def test_import_file_with_import_error(self, tmp_path, console): assert exc_info.value.code == 1 - def test_import_module_none_spec(self, tmp_path, console): + def test_import_module_none_spec(self, tmp_path: Path, console: Console) -> None: """Test case where spec_from_file_location returns None.""" # This is extremely rare, but can happen with certain file paths or conditions # Testing with a directory instead of a file might trigger this @@ -217,21 +225,23 @@ def test_import_module_none_spec(self, tmp_path, console): class TestCliError: """Tests for cli_error function.""" - def test_cli_error_exits(self, console): + def test_cli_error_exits(self, console: Console) -> None: """Test that cli_error exits the program.""" with pytest.raises(SystemExit) as exc_info: cli_error(console, "Test error message") assert exc_info.value.code == 1 - def test_cli_error_custom_exit_code(self, console): + def test_cli_error_custom_exit_code(self, console: Console) -> None: """Test cli_error with a custom exit code.""" with pytest.raises(SystemExit) as exc_info: cli_error(console, "Test error message", exit_code=2) assert exc_info.value.code == 2 - def test_cli_error_adds_prefix(self, capsys, console): + def test_cli_error_adds_prefix( + self, capsys: pytest.CaptureFixture[str], console: Console + ) -> None: """Test that ERROR: prefix is added if not present.""" with pytest.raises(SystemExit): cli_error(console, "Test message") @@ -239,7 +249,9 @@ def test_cli_error_adds_prefix(self, capsys, console): captured = capsys.readouterr() assert "ERROR: Test message" in captured.err - def test_cli_error_preserves_prefix(self, capsys, console): + def test_cli_error_preserves_prefix( + self, capsys: pytest.CaptureFixture[str], console: Console + ) -> None: """Test that existing ERROR: prefix is not duplicated.""" with pytest.raises(SystemExit): cli_error(console, "ERROR: Already has prefix") @@ -252,19 +264,23 @@ def test_cli_error_preserves_prefix(self, capsys, console): class TestCliWarning: """Tests for cli_warning function.""" - def test_cli_warning_does_not_exit(self, console): + def test_cli_warning_does_not_exit(self, console: Console) -> None: """Test that cli_warning does not exit the program.""" # Should not raise SystemExit cli_warning(console, "Test warning message") - def test_cli_warning_adds_prefix(self, capsys, console): + def test_cli_warning_adds_prefix( + self, capsys: pytest.CaptureFixture[str], console: Console + ) -> None: """Test that Warning: prefix is added if not present.""" cli_warning(console, "Test message") captured = capsys.readouterr() assert "Warning: Test message" in captured.err - def test_cli_warning_preserves_prefix(self, capsys, console): + def test_cli_warning_preserves_prefix( + self, capsys: pytest.CaptureFixture[str], console: Console + ) -> None: """Test that existing Warning: prefix is not duplicated.""" cli_warning(console, "Warning: Already has prefix") @@ -272,7 +288,9 @@ def test_cli_warning_preserves_prefix(self, capsys, console): assert "Warning: Warning:" not in captured.err assert "Warning: Already has prefix" in captured.err - def test_multiple_warnings(self, capsys, console): + def test_multiple_warnings( + self, capsys: pytest.CaptureFixture[str], console: Console + ) -> None: """Test that multiple warnings can be issued.""" cli_warning(console, "First warning") cli_warning(console, "Second warning") @@ -285,7 +303,7 @@ def test_multiple_warnings(self, capsys, console): class TestValidateInputFile: """Tests for validate_input_file function.""" - def test_validate_existing_file(self, tmp_path, console): + def test_validate_existing_file(self, tmp_path: Path, console: Console) -> None: """Test validating an existing readable file.""" test_file = tmp_path / "test.txt" test_file.write_text("content") @@ -293,7 +311,7 @@ def test_validate_existing_file(self, tmp_path, console): # Should not raise validate_input_file(console, test_file) - def test_validate_nonexistent_file(self, tmp_path, console): + def test_validate_nonexistent_file(self, tmp_path: Path, console: Console) -> None: """Test validating a nonexistent file.""" test_file = tmp_path / "nonexistent.txt" @@ -302,14 +320,18 @@ def test_validate_nonexistent_file(self, tmp_path, console): assert exc_info.value.code == 1 - def test_validate_directory_not_file(self, tmp_path, console): + def test_validate_directory_not_file( + self, tmp_path: Path, console: Console + ) -> None: """Test validating a directory instead of a file.""" with pytest.raises(SystemExit) as exc_info: validate_input_file(console, tmp_path) assert exc_info.value.code == 1 - def test_validate_custom_description(self, tmp_path, console): + def test_validate_custom_description( + self, tmp_path: Path, console: Console + ) -> None: """Test validate_input_file with custom file description.""" test_file = tmp_path / "nonexistent.txt" @@ -320,7 +342,7 @@ def test_validate_custom_description(self, tmp_path, console): # Error message should contain custom description - def test_validate_unreadable_file(self, tmp_path, console): + def test_validate_unreadable_file(self, tmp_path: Path, console: Console) -> None: """Test validating a file without read permissions.""" if sys.platform == "win32": pytest.skip("File permission tests unreliable on Windows") @@ -344,14 +366,16 @@ def test_validate_unreadable_file(self, tmp_path, console): class TestValidateOutputPath: """Tests for validate_output_path function.""" - def test_validate_new_output_path(self, tmp_path, console): + def test_validate_new_output_path(self, tmp_path: Path, console: Console) -> None: """Test validating a new output path (file doesn't exist).""" output_file = tmp_path / "output.txt" # Should not raise validate_output_path(console, output_file, overwrite=False) - def test_validate_existing_path_without_overwrite(self, tmp_path, console): + def test_validate_existing_path_without_overwrite( + self, tmp_path: Path, console: Console + ) -> None: """Test validating existing file without overwrite permission.""" output_file = tmp_path / "existing.txt" output_file.write_text("existing content") @@ -361,7 +385,9 @@ def test_validate_existing_path_without_overwrite(self, tmp_path, console): assert exc_info.value.code == 1 - def test_validate_existing_path_with_overwrite(self, tmp_path, console): + def test_validate_existing_path_with_overwrite( + self, tmp_path: Path, console: Console + ) -> None: """Test validating existing file with overwrite permission.""" output_file = tmp_path / "existing.txt" output_file.write_text("existing content") @@ -373,37 +399,37 @@ def test_validate_existing_path_with_overwrite(self, tmp_path, console): class TestFormatLineRanges: """Tests for format_line_ranges function.""" - def test_format_empty_list(self): + def test_format_empty_list(self) -> None: """Test formatting an empty list.""" result = format_line_ranges([]) assert not result - def test_format_single_line(self): + def test_format_single_line(self) -> None: """Test formatting a single line number.""" result = format_line_ranges([42]) assert result == ["42"] - def test_format_consecutive_lines(self): + def test_format_consecutive_lines(self) -> None: """Test formatting consecutive line numbers.""" result = format_line_ranges([1, 2, 3, 4, 5]) assert result == ["1-5"] - def test_format_mixed_ranges(self): + def test_format_mixed_ranges(self) -> None: """Test formatting mixed consecutive and individual lines.""" result = format_line_ranges([1, 2, 3, 5, 6, 8, 10, 11, 12]) assert result == ["1-3", "5-6", "8", "10-12"] - def test_format_unsorted_lines(self): + def test_format_unsorted_lines(self) -> None: """Test formatting unsorted line numbers.""" result = format_line_ranges([5, 1, 3, 2, 8]) assert result == ["1-3", "5", "8"] - def test_format_duplicate_lines(self): + def test_format_duplicate_lines(self) -> None: """Test formatting with duplicate line numbers.""" result = format_line_ranges([1, 2, 2, 3, 5, 5, 6]) @@ -411,19 +437,19 @@ def test_format_duplicate_lines(self): assert len(result) == 4 # ['1-2', '2-3', '5', '5-6'] assert "1-2" in result or "2-3" in result - def test_format_large_range(self): + def test_format_large_range(self) -> None: """Test formatting a large range of consecutive lines.""" result = format_line_ranges(list(range(100, 200))) assert result == ["100-199"] - def test_format_all_individual_lines(self): + def test_format_all_individual_lines(self) -> None: """Test formatting non-consecutive lines.""" result = format_line_ranges([1, 5, 10, 15, 20]) assert result == ["1", "5", "10", "15", "20"] - def test_format_two_consecutive_lines(self): + def test_format_two_consecutive_lines(self) -> None: """Test formatting exactly two consecutive lines.""" result = format_line_ranges([5, 6]) diff --git a/tests/fctools/test_coverage_summary.py b/tests/fctools/test_coverage_summary.py index 3606fd100..c98930f1e 100644 --- a/tests/fctools/test_coverage_summary.py +++ b/tests/fctools/test_coverage_summary.py @@ -3,9 +3,14 @@ Tests the coverage analysis and reporting functionality. """ +# pylint: disable=too-many-lines + import json +from pathlib import Path +from typing import Any import pytest +from pytest import CaptureFixture from rich.console import Console from typer.testing import CliRunner @@ -29,7 +34,7 @@ @pytest.fixture -def console(): +def console() -> Console: """Return a rich console object for testing.""" return Console() @@ -37,7 +42,7 @@ def console(): class TestCoverageSummary: """Tests for CoverageSummary dataclass.""" - def test_default_values(self): + def test_default_values(self) -> None: """Test that CoverageSummary has correct default values.""" summary = CoverageSummary() @@ -54,7 +59,7 @@ def test_default_values(self): assert summary.total_branches == 0 assert summary.total_missing_branches == 0 - def test_custom_values(self): + def test_custom_values(self) -> None: """Test creating CoverageSummary with custom values.""" summary = CoverageSummary( total_files=10, @@ -70,7 +75,7 @@ def test_custom_values(self): class TestFileIssue: """Tests for FileIssue dataclass.""" - def test_file_issue_creation(self): + def test_file_issue_creation(self) -> None: """Test creating a FileIssue object.""" issue = FileIssue( file_path="test.py", @@ -97,7 +102,7 @@ def test_file_issue_creation(self): class TestCalculateBranchCoverageSummary: """Tests for _calculate_branch_coverage_summary function.""" - def test_with_branches(self): + def test_with_branches(self) -> None: """Test branch coverage calculation with branches present.""" totals = {"num_branches": 100, "missing_branches": 20} @@ -107,7 +112,7 @@ def test_with_branches(self): assert missing == 20 assert coverage == 80.0 - def test_with_no_branches(self): + def test_with_no_branches(self) -> None: """Test branch coverage when no branches exist.""" totals = {"num_branches": 0, "missing_branches": 0} @@ -117,7 +122,7 @@ def test_with_no_branches(self): assert missing == 0 assert coverage == 100.0 - def test_missing_keys(self): + def test_missing_keys(self) -> None: """Test with missing dictionary keys.""" totals: dict[str, int] = {} @@ -127,7 +132,7 @@ def test_missing_keys(self): assert missing == 0 assert coverage == 100.0 - def test_perfect_branch_coverage(self): + def test_perfect_branch_coverage(self) -> None: """Test with 100% branch coverage.""" totals = {"num_branches": 50, "missing_branches": 0} @@ -141,7 +146,7 @@ def test_perfect_branch_coverage(self): class TestCalculateFileBranchCoverage: """Tests for _calculate_file_branch_coverage function.""" - def test_with_branches(self): + def test_with_branches(self) -> None: """Test file branch coverage with branches present.""" file_summary = {"num_branches": 10, "covered_branches": 8} @@ -149,7 +154,7 @@ def test_with_branches(self): assert coverage == 80.0 - def test_no_branches_with_statements(self): + def test_no_branches_with_statements(self) -> None: """Test file with no branches but has statements.""" file_summary = {"num_branches": 0} @@ -157,7 +162,7 @@ def test_no_branches_with_statements(self): assert coverage == 100.0 - def test_no_branches_no_statements(self): + def test_no_branches_no_statements(self) -> None: """Test file with no branches and no statements.""" file_summary = {"num_branches": 0} @@ -165,7 +170,7 @@ def test_no_branches_no_statements(self): assert coverage == 0.0 - def test_perfect_branch_coverage(self): + def test_perfect_branch_coverage(self) -> None: """Test file with 100% branch coverage.""" file_summary = {"num_branches": 20, "covered_branches": 20} @@ -173,7 +178,7 @@ def test_perfect_branch_coverage(self): assert coverage == 100.0 - def test_missing_keys(self): + def test_missing_keys(self) -> None: """Test with missing dictionary keys.""" file_summary: dict[str, int] = {} @@ -185,7 +190,7 @@ def test_missing_keys(self): class TestCreateFileIssue: """Tests for _create_file_issue function.""" - def test_create_file_issue(self): + def test_create_file_issue(self) -> None: """Test creating a FileIssue from coverage data.""" file_summary = {"num_statements": 100, "num_branches": 20} missing_lines = [10, 20, 30] @@ -218,7 +223,7 @@ def test_create_file_issue(self): class TestAnalyzeSingleFile: """Tests for _analyze_single_file function.""" - def test_perfect_coverage_file(self): + def test_perfect_coverage_file(self) -> None: """Test analyzing a file with perfect coverage.""" summary = CoverageSummary() file_data = { @@ -242,7 +247,7 @@ def test_perfect_coverage_file(self): assert summary.files_with_missing_branches == 0 assert summary.files_with_excluded_lines == 0 - def test_file_with_missing_lines(self): + def test_file_with_missing_lines(self) -> None: """Test analyzing a file with missing lines.""" summary = CoverageSummary() file_data = { @@ -265,7 +270,7 @@ def test_file_with_missing_lines(self): assert summary.files_with_perfect_coverage == 0 assert summary.files_with_missing_lines == 1 - def test_file_with_missing_branches(self): + def test_file_with_missing_branches(self) -> None: """Test analyzing a file with missing branches.""" summary = CoverageSummary() file_data = { @@ -286,7 +291,7 @@ def test_file_with_missing_branches(self): assert result.missing_branches_count == 2 assert summary.files_with_missing_branches == 1 - def test_file_with_excluded_lines_only(self): + def test_file_with_excluded_lines_only(self) -> None: """Test analyzing a file with only excluded lines (no missing coverage).""" summary = CoverageSummary() file_data = { @@ -307,7 +312,7 @@ def test_file_with_excluded_lines_only(self): assert summary.files_with_excluded_lines == 1 assert summary.files_with_perfect_coverage == 1 # Still perfect coverage - def test_file_with_all_issues(self): + def test_file_with_all_issues(self) -> None: """Test file with missing lines, branches, and excluded lines.""" summary = CoverageSummary() file_data = { @@ -334,7 +339,7 @@ def test_file_with_all_issues(self): class TestAnalyzeCoverageJson: """Tests for analyze_coverage_json function.""" - def test_analyze_simple_coverage(self, tmp_path, console): + def test_analyze_simple_coverage(self, tmp_path: Path, console: Console) -> None: """Test analyzing a simple coverage JSON file.""" coverage_data = { "files": { @@ -382,7 +387,9 @@ def test_analyze_simple_coverage(self, tmp_path, console): assert len(file_issues) == 1 assert file_issues[0].file_path == "file1.py" - def test_file_issues_sorted_by_coverage(self, tmp_path, console): + def test_file_issues_sorted_by_coverage( + self, tmp_path: Path, console: Console + ) -> None: """Test that file issues are sorted by coverage (worst first).""" coverage_data = { "files": { @@ -437,7 +444,7 @@ def test_file_issues_sorted_by_coverage(self, tmp_path, console): assert file_issues[1].file_path == "file3.py" # Middle: 75% assert file_issues[2].file_path == "file1.py" # Best: 90% - def test_empty_coverage_file(self, tmp_path, console): + def test_empty_coverage_file(self, tmp_path: Path, console: Console) -> None: """Test analyzing an empty coverage file.""" coverage_data: dict[str, dict[str, object]] = {"files": {}, "totals": {}} @@ -453,7 +460,7 @@ def test_empty_coverage_file(self, tmp_path, console): class TestPrintCoverageSummary: """Tests for print_coverage_summary function.""" - def test_print_summary(self, capsys, console): + def test_print_summary(self, capsys: CaptureFixture[str], console: Console) -> None: """Test printing coverage summary.""" summary = CoverageSummary( total_files=10, @@ -485,7 +492,9 @@ def test_print_summary(self, capsys, console): class TestPrintSourceCodeForMissingLines: """Tests for _print_source_code_for_missing_lines function.""" - def test_print_source_existing_file(self, tmp_path, capsys, console): + def test_print_source_existing_file( + self, tmp_path: Path, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing source code for existing file.""" source_file = tmp_path / "test.py" source_file.write_text("line 1\nline 2\nline 3\nline 4\nline 5\n") @@ -511,7 +520,9 @@ def test_print_source_existing_file(self, tmp_path, capsys, console): assert "2: line 2" in captured.out assert "4: line 4" in captured.out - def test_print_source_nonexistent_file(self, capsys, console): + def test_print_source_nonexistent_file( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test handling of nonexistent source file.""" issue = FileIssue( file_path="/nonexistent/file.py", @@ -532,7 +543,9 @@ def test_print_source_nonexistent_file(self, capsys, console): captured = capsys.readouterr() assert "(Source file not found for line details)" in captured.out - def test_print_source_with_unicode_error(self, tmp_path, capsys, console): + def test_print_source_with_unicode_error( + self, tmp_path: Path, capsys: CaptureFixture[str], console: Console + ) -> None: """Test handling of files with encoding errors.""" source_file = tmp_path / "test.py" # Write binary data that's not valid UTF-8 @@ -557,7 +570,9 @@ def test_print_source_with_unicode_error(self, tmp_path, capsys, console): captured = capsys.readouterr() assert "(Error reading source file:" in captured.out - def test_print_source_line_out_of_range(self, tmp_path, capsys, console): + def test_print_source_line_out_of_range( + self, tmp_path: Path, capsys: CaptureFixture[str], console: Console + ) -> None: """Test handling of line numbers outside file range.""" source_file = tmp_path / "test.py" source_file.write_text("line 1\nline 2\n") @@ -586,7 +601,9 @@ def test_print_source_line_out_of_range(self, tmp_path, capsys, console): class TestPrintFileIssueDetails: """Tests for _print_file_issue_details function.""" - def test_print_with_missing_lines_and_branches(self, tmp_path, capsys, console): + def test_print_with_missing_lines_and_branches( + self, tmp_path: Path, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing file issue with missing lines and branches.""" source_file = tmp_path / "test.py" source_file.write_text("line 1\nline 2\nline 3\n") @@ -616,7 +633,9 @@ def test_print_with_missing_lines_and_branches(self, tmp_path, capsys, console): assert "Missing Branches (1):" in captured.out assert "[10, 11]" in captured.out - def test_print_without_showing_source(self, capsys, console): + def test_print_without_showing_source( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing without showing source code.""" issue = FileIssue( file_path="test.py", @@ -639,7 +658,9 @@ def test_print_without_showing_source(self, capsys, console): assert "Missing Lines (1):" in captured.out assert "Source code for missing lines:" not in captured.out - def test_print_with_no_branches(self, capsys, console): + def test_print_with_no_branches( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing for file with no branches.""" issue = FileIssue( file_path="test.py", @@ -662,7 +683,9 @@ def test_print_with_no_branches(self, capsys, console): assert "Branch Coverage:" not in captured.out # Should not show for 0 branches assert "Excluded Lines (1):" in captured.out - def test_print_with_excluded_lines(self, capsys, console): + def test_print_with_excluded_lines( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing with excluded lines.""" issue = FileIssue( file_path="test.py", @@ -688,14 +711,18 @@ def test_print_with_excluded_lines(self, capsys, console): class TestPrintFileIssues: """Tests for print_file_issues function.""" - def test_print_no_issues(self, capsys, console): + def test_print_no_issues( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing when no issues exist.""" print_file_issues(console, []) captured = capsys.readouterr() assert "ALL FILES HAVE PERFECT COVERAGE!" in captured.out - def test_print_multiple_issues(self, capsys, console): + def test_print_multiple_issues( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing multiple file issues.""" issues = [ FileIssue( @@ -737,7 +764,9 @@ def test_print_multiple_issues(self, capsys, console): class TestPrintPerfectCoverageFiles: """Tests for print_perfect_coverage_files function.""" - def test_print_perfect_files(self, capsys, console): + def test_print_perfect_files( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test printing files with perfect coverage.""" file_issues = [ FileIssue( @@ -764,7 +793,9 @@ def test_print_perfect_files(self, capsys, console): assert "✅ good2.py" in captured.out assert "bad.py" not in captured.out - def test_no_perfect_files(self, capsys, console): + def test_no_perfect_files( + self, capsys: CaptureFixture[str], console: Console + ) -> None: """Test when no files have perfect coverage.""" file_issues = [ FileIssue( @@ -793,7 +824,7 @@ def test_no_perfect_files(self, capsys, console): class TestMainFunction: # pylint: disable=import-outside-toplevel """Tests for main CLI function.""" - def test_main_basic_usage(self, tmp_path): + def test_main_basic_usage(self, tmp_path: Path) -> None: """Test main function with basic coverage file.""" coverage_data = { "files": { @@ -829,7 +860,7 @@ def test_main_basic_usage(self, tmp_path): assert match_wrapped(result.stdout, "COVERAGE ANALYSIS SUMMARY") assert match_wrapped(result.stdout, "Total files analyzed: 1") - def test_main_with_show_source(self, tmp_path): + def test_main_with_show_source(self, tmp_path: Path) -> None: """Test main function with --show-source flag.""" source_file = tmp_path / "test.py" source_file.write_text("line 1\nline 2\n") @@ -867,7 +898,7 @@ def test_main_with_show_source(self, tmp_path): assert match_wrapped(result.stdout, "Source code for missing lines:") - def test_main_with_show_perfect(self, tmp_path): + def test_main_with_show_perfect(self, tmp_path: Path) -> None: """Test main function with --show-perfect flag.""" coverage_data = { "files": { @@ -913,20 +944,24 @@ def test_main_with_show_perfect(self, tmp_path): assert match_wrapped(result.stdout, "FILES WITH PERFECT COVERAGE:") assert match_wrapped(result.stdout, "perfect.py") - def test_main_with_nonexistent_file(self, tmp_path): + def test_main_with_nonexistent_file(self, tmp_path: Path) -> None: """Test main function with nonexistent coverage file.""" runner = CliRunner() result = runner.invoke(app, [str(tmp_path / "nonexistent.json")]) assert result.exit_code != 0 - def test_main_with_os_error(self, tmp_path, monkeypatch): + def test_main_with_os_error( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: """Test main function handling of OSError.""" coverage_file = tmp_path / "coverage.json" coverage_file.write_text('{"files": {}, "totals": {}}') # Mock load_json_file to raise OSError - def mock_load_json_file(*args, **kwargs): + def mock_load_json_file( + console: Console, file_path: Path, error_context: str = "reading file" + ) -> dict[str, Any]: raise OSError("Simulated OS error") from firecrown.fctools import coverage_summary @@ -940,7 +975,7 @@ def mock_load_json_file(*args, **kwargs): assert match_wrapped(result.stdout, "Error analyzing coverage file:") - def test_main_block_with_subprocess(self, tmp_path): + def test_main_block_with_subprocess(self, tmp_path: Path) -> None: """Test that the script can be executed directly via subprocess. This test verifies that the __main__ block and ImportError fallback diff --git a/tests/fctools/test_coverage_to_tsv_cli.py b/tests/fctools/test_coverage_to_tsv_cli.py index 5342042d8..832224173 100644 --- a/tests/fctools/test_coverage_to_tsv_cli.py +++ b/tests/fctools/test_coverage_to_tsv_cli.py @@ -13,7 +13,7 @@ from . import match_wrapped -def test_main_basic(tmp_path): +def test_main_basic(tmp_path: Path) -> None: coverage_file = tmp_path / "coverage.json" output_file = tmp_path / "output.tsv" @@ -70,7 +70,7 @@ def test_main_basic(tmp_path): assert "src/example.py" in lines[1] -def test_main_with_timing(tmp_path): +def test_main_with_timing(tmp_path: Path) -> None: coverage_file = tmp_path / "coverage.json" timing_file = tmp_path / "timing.json" output_file = tmp_path / "output.tsv" @@ -132,7 +132,7 @@ def test_main_with_timing(tmp_path): assert match_wrapped(result.stdout, "Records with timing data:") -def test_main_default_output_filename(tmp_path): +def test_main_default_output_filename(tmp_path: Path) -> None: coverage_file = tmp_path / "coverage.json" coverage_data = { @@ -183,7 +183,7 @@ def test_main_default_output_filename(tmp_path): assert default_output.exists() -def test_main_nonexistent_input(tmp_path): +def test_main_nonexistent_input(tmp_path: Path) -> None: nonexistent_file = tmp_path / "does_not_exist.json" output_file = tmp_path / "output.tsv" @@ -203,7 +203,7 @@ def test_main_nonexistent_input(tmp_path): assert match_wrapped(result.stderr.lower(), "does not exist") -def test_main_invalid_json(tmp_path): +def test_main_invalid_json(tmp_path: Path) -> None: coverage_file = tmp_path / "invalid.json" output_file = tmp_path / "output.tsv" @@ -225,7 +225,7 @@ def test_main_invalid_json(tmp_path): assert "json" in result.stderr.lower() -def test_main_missing_expected_key(tmp_path): +def test_main_missing_expected_key(tmp_path: Path) -> None: coverage_file = tmp_path / "missing_key.json" output_file = tmp_path / "output.tsv" @@ -257,7 +257,7 @@ def test_main_missing_expected_key(tmp_path): assert len(lines) == 1 -def test_main_write_error(tmp_path): +def test_main_write_error(tmp_path: Path) -> None: coverage_file = tmp_path / "coverage.json" coverage_data = { @@ -310,7 +310,7 @@ def test_main_write_error(tmp_path): assert match_wrapped(result.stdout, "Error: File operation failed") -def test_main_timing_file_not_found(tmp_path): +def test_main_timing_file_not_found(tmp_path: Path) -> None: coverage_file = tmp_path / "coverage.json" output_file = tmp_path / "output.tsv" nonexistent_timing = tmp_path / "timing_does_not_exist.txt" diff --git a/tests/fctools/test_coverage_to_tsv_data.py b/tests/fctools/test_coverage_to_tsv_data.py index 55f46c2b7..3aa82ef8d 100644 --- a/tests/fctools/test_coverage_to_tsv_data.py +++ b/tests/fctools/test_coverage_to_tsv_data.py @@ -6,6 +6,8 @@ # pylint: disable=missing-function-docstring +from pathlib import Path + import pytest from firecrown.fctools.coverage_to_tsv import ( @@ -18,7 +20,7 @@ # Tests for match_test_to_function() -def test_match_test_to_function_exact_name_match(): +def test_match_test_to_function_exact_name_match() -> None: test_name = "tests/test_module.py::test_my_function" function_name = "my_function" file_path = "src/module.py" @@ -29,7 +31,7 @@ def test_match_test_to_function_exact_name_match(): assert score >= 0.5 -def test_match_test_to_function_file_match(): +def test_match_test_to_function_file_match() -> None: test_name = "tests/test_example.py::test_something" function_name = "other_function" file_path = "src/example.py" @@ -40,7 +42,7 @@ def test_match_test_to_function_file_match(): assert score == pytest.approx(0.3) -def test_match_test_to_function_full_match(): +def test_match_test_to_function_full_match() -> None: test_name = "tests/test_utils.py::test_format_string" function_name = "format_string" file_path = "src/utils.py" @@ -54,7 +56,7 @@ def test_match_test_to_function_full_match(): assert score == 1.0 -def test_match_test_to_function_no_match(): +def test_match_test_to_function_no_match() -> None: test_name = "tests/test_other.py::test_something" function_name = "unrelated_function" file_path = "src/different.py" @@ -64,7 +66,7 @@ def test_match_test_to_function_no_match(): assert score == 0.0 -def test_match_test_to_function_test_name_no_separator(): +def test_match_test_to_function_test_name_no_separator() -> None: test_name = "tests/test_module.py" function_name = "some_function" file_path = "src/module.py" @@ -79,7 +81,7 @@ def test_match_test_to_function_test_name_no_separator(): assert score >= 0.3 -def test_match_test_to_function_case_insensitive(): +def test_match_test_to_function_case_insensitive() -> None: test_name = "tests/test_module.py::test_MyFunction" function_name = "myfunction" file_path = "src/other.py" @@ -93,7 +95,7 @@ def test_match_test_to_function_case_insensitive(): # Tests for extract_coverage_data() -def test_extract_coverage_data_basic(): +def test_extract_coverage_data_basic() -> None: coverage_data = { "files": { "src/example.py": { @@ -144,7 +146,7 @@ def test_extract_coverage_data_basic(): assert record.test_duration is None -def test_extract_coverage_data_with_timing(): +def test_extract_coverage_data_with_timing() -> None: coverage_data = { "files": { "src/utils.py": { @@ -186,7 +188,7 @@ def test_extract_coverage_data_with_timing(): assert record.test_duration == 0.25 -def test_extract_coverage_data_timing_below_threshold(): +def test_extract_coverage_data_timing_below_threshold() -> None: coverage_data = { "files": { "src/module.py": { @@ -227,7 +229,7 @@ def test_extract_coverage_data_timing_below_threshold(): assert record.test_duration is None -def test_extract_coverage_data_multiple_functions(): +def test_extract_coverage_data_multiple_functions() -> None: coverage_data = { "files": { "src/module.py": { @@ -277,7 +279,7 @@ def test_extract_coverage_data_multiple_functions(): assert func_names == {"func_a", "func_b"} -def test_extract_coverage_data_no_functions(): +def test_extract_coverage_data_no_functions() -> None: coverage_data = { "files": { "src/empty.py": { @@ -295,7 +297,7 @@ def test_extract_coverage_data_no_functions(): assert len(result) == 0 -def test_extract_coverage_data_empty_files(): +def test_extract_coverage_data_empty_files() -> None: coverage_data: dict[str, dict[str, object]] = {"files": {}} result = extract_coverage_data(coverage_data) @@ -303,7 +305,7 @@ def test_extract_coverage_data_empty_files(): assert len(result) == 0 -def test_extract_coverage_data_missing_summary_fields(): +def test_extract_coverage_data_missing_summary_fields() -> None: coverage_data: dict[str, dict[str, object]] = { "files": { "src/partial.py": { @@ -326,7 +328,7 @@ def test_extract_coverage_data_missing_summary_fields(): # Tests for write_tsv_file() -def test_write_tsv_file(tmp_path): +def test_write_tsv_file(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( @@ -368,7 +370,7 @@ def test_write_tsv_file(tmp_path): assert data_row[-1] == "0.25" -def test_write_tsv_file_no_timing(tmp_path): +def test_write_tsv_file_no_timing(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( @@ -411,7 +413,7 @@ def test_write_tsv_file_no_timing(tmp_path): assert data_row[-2] == "100.0" -def test_write_tsv_file_multiple_records(tmp_path): +def test_write_tsv_file_multiple_records(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( diff --git a/tests/fctools/test_coverage_to_tsv_output.py b/tests/fctools/test_coverage_to_tsv_output.py index 6e30fbbfb..7e5101358 100644 --- a/tests/fctools/test_coverage_to_tsv_output.py +++ b/tests/fctools/test_coverage_to_tsv_output.py @@ -10,6 +10,8 @@ import sys from pathlib import Path +import pytest + from firecrown.fctools.coverage_to_tsv import CoverageRecord, main, write_tsv_file from . import match_wrapped @@ -17,7 +19,7 @@ # Tests for write_tsv_file() -def test_write_tsv_file(tmp_path): +def test_write_tsv_file(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( @@ -59,7 +61,7 @@ def test_write_tsv_file(tmp_path): assert data_row[-1] == "0.25" -def test_write_tsv_file_no_timing(tmp_path): +def test_write_tsv_file_no_timing(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( @@ -102,7 +104,7 @@ def test_write_tsv_file_no_timing(tmp_path): assert data_row[-2] == "100.0" -def test_write_tsv_file_multiple_records(tmp_path): +def test_write_tsv_file_multiple_records(tmp_path: Path) -> None: output_file = tmp_path / "output.tsv" data = [ CoverageRecord( @@ -154,7 +156,7 @@ def test_write_tsv_file_multiple_records(tmp_path): # Tests for main() -def test_main_basic(tmp_path): +def test_main_basic(tmp_path: Path) -> None: # Create input file input_file = tmp_path / "coverage.json" coverage_data = { @@ -204,7 +206,7 @@ def test_main_basic(tmp_path): assert match_wrapped(result.stdout, "Records written: 1") -def test_main_with_timing(tmp_path): +def test_main_with_timing(tmp_path: Path) -> None: # Create input file input_file = tmp_path / "coverage.json" coverage_data = { @@ -268,7 +270,9 @@ def test_main_with_timing(tmp_path): assert match_wrapped(result.stdout, "Records with timing data:") -def test_main_default_output_filename(tmp_path, monkeypatch): +def test_main_default_output_filename( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: # Get absolute path to script before changing directory script_path = Path("firecrown/fctools/coverage_to_tsv.py").absolute() @@ -317,7 +321,7 @@ def test_main_default_output_filename(tmp_path, monkeypatch): assert Path("coverage_data.tsv").exists() -def test_main_nonexistent_input(tmp_path): +def test_main_nonexistent_input(tmp_path: Path) -> None: input_file = tmp_path / "nonexistent.json" output_file = tmp_path / "output.tsv" @@ -334,7 +338,7 @@ def test_main_nonexistent_input(tmp_path): assert match_wrapped(result.stderr.lower(), "does not exist") -def test_main_invalid_json(tmp_path): +def test_main_invalid_json(tmp_path: Path) -> None: input_file = tmp_path / "invalid.json" input_file.write_text("This is not valid JSON") @@ -352,7 +356,7 @@ def test_main_invalid_json(tmp_path): assert "ERROR" in result.stderr -def test_main_missing_expected_key(tmp_path): +def test_main_missing_expected_key(tmp_path: Path) -> None: input_file = tmp_path / "bad_structure.json" # Missing 'files' key that code expects input_file.write_text('{"other_key": "value"}') @@ -372,7 +376,7 @@ def test_main_missing_expected_key(tmp_path): assert match_wrapped(result.stdout, "Records written: 0") -def test_main_write_error(tmp_path): +def test_main_write_error(tmp_path: Path) -> None: input_file = tmp_path / "coverage.json" coverage_data = { "files": { @@ -420,7 +424,7 @@ def test_main_write_error(tmp_path): assert match_wrapped(result.stdout, "Error: File operation failed") -def test_main_timing_file_not_found(tmp_path): +def test_main_timing_file_not_found(tmp_path: Path) -> None: input_file = tmp_path / "coverage.json" coverage_data = { "files": { @@ -476,7 +480,7 @@ def test_main_timing_file_not_found(tmp_path): # Tests for main() function called directly (for coverage of console output) -def test_main_direct_basic(tmp_path, capsys): +def test_main_direct_basic(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: """Test main function called directly for coverage of success path console output. This test calls main() directly (not via subprocess) to ensure coverage @@ -538,7 +542,9 @@ def test_main_direct_basic(tmp_path, capsys): assert match_wrapped(captured.out, "Records written: 1") -def test_main_direct_with_timing(tmp_path, capsys): +def test_main_direct_with_timing( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with timing data for coverage of timing-related output. Tests the branch where timing_data is not None (lines 367-370). diff --git a/tests/fctools/test_coverage_to_tsv_parsing.py b/tests/fctools/test_coverage_to_tsv_parsing.py index 853980715..fef319717 100644 --- a/tests/fctools/test_coverage_to_tsv_parsing.py +++ b/tests/fctools/test_coverage_to_tsv_parsing.py @@ -6,6 +6,7 @@ # pylint: disable=missing-function-docstring import json +from pathlib import Path import pytest from rich.console import Console @@ -20,7 +21,7 @@ # Tests for _parse_duration_line() -def test_parse_duration_line_valid(): +def test_parse_duration_line_valid() -> None: line = "0.12s call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -29,7 +30,7 @@ def test_parse_duration_line_valid(): assert duration == 0.12 -def test_parse_duration_line_with_setup(): +def test_parse_duration_line_with_setup() -> None: line = "0.05s setup tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -38,7 +39,7 @@ def test_parse_duration_line_with_setup(): assert duration == 0.05 -def test_parse_duration_line_with_teardown(): +def test_parse_duration_line_with_teardown() -> None: line = "0.03s teardown tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -47,7 +48,7 @@ def test_parse_duration_line_with_teardown(): assert duration == 0.03 -def test_parse_duration_line_integer_duration(): +def test_parse_duration_line_integer_duration() -> None: line = "1s call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -56,7 +57,7 @@ def test_parse_duration_line_integer_duration(): assert duration == 1.0 -def test_parse_duration_line_no_phase(): +def test_parse_duration_line_no_phase() -> None: line = "0.25s tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -65,19 +66,19 @@ def test_parse_duration_line_no_phase(): assert duration == 0.25 -def test_parse_duration_line_invalid(): +def test_parse_duration_line_invalid() -> None: line = "This is not a valid duration line" result = _parse_duration_line(line) assert result is None -def test_parse_duration_line_no_duration(): +def test_parse_duration_line_no_duration() -> None: line = "tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is None -def test_parse_duration_line_invalid_duration_format(): +def test_parse_duration_line_invalid_duration_format() -> None: line = "abc call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is None @@ -86,7 +87,7 @@ def test_parse_duration_line_invalid_duration_format(): # Tests for _load_text_durations() -def test_load_text_durations(tmp_path): +def test_load_text_durations(tmp_path: Path) -> None: timing_file = tmp_path / "durations.txt" timing_file.write_text( "0.12s call tests/test_example.py::test_func1\n" @@ -106,7 +107,7 @@ def test_load_text_durations(tmp_path): assert result["tests/test_example.py::test_func2"] == pytest.approx(0.08) -def test_load_text_durations_empty_file(tmp_path): +def test_load_text_durations_empty_file(tmp_path: Path) -> None: timing_file = tmp_path / "empty.txt" timing_file.write_text("") @@ -114,7 +115,7 @@ def test_load_text_durations_empty_file(tmp_path): assert not result -def test_load_text_durations_all_invalid(tmp_path): +def test_load_text_durations_all_invalid(tmp_path: Path) -> None: timing_file = tmp_path / "invalid.txt" timing_file.write_text("Invalid line 1\nInvalid line 2\nInvalid line 3\n") @@ -125,7 +126,7 @@ def test_load_text_durations_all_invalid(tmp_path): # Tests for _load_json_timing() -def test_load_json_timing(tmp_path): +def test_load_json_timing(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -143,7 +144,7 @@ def test_load_json_timing(tmp_path): assert result["tests/test_b.py::test_three"] == 0.3 -def test_load_json_timing_missing_fields(tmp_path): +def test_load_json_timing_missing_fields(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -161,7 +162,7 @@ def test_load_json_timing_missing_fields(tmp_path): assert result["tests/test_b.py::test_two"] == 0.0 -def test_load_json_timing_invalid_duration(tmp_path): +def test_load_json_timing_invalid_duration(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -175,7 +176,7 @@ def test_load_json_timing_invalid_duration(tmp_path): assert result["tests/test_a.py::test_one"] == 0.0 -def test_load_json_timing_no_tests_key(tmp_path): +def test_load_json_timing_no_tests_key(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = {"summary": "some data", "other_field": []} timing_file.write_text(json.dumps(data)) @@ -188,7 +189,7 @@ def test_load_json_timing_no_tests_key(tmp_path): # Tests for parse_timing_data() -def test_parse_timing_data_json_format(tmp_path): +def test_parse_timing_data_json_format(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "timing.json" data = { @@ -203,7 +204,7 @@ def test_parse_timing_data_json_format(tmp_path): assert result["tests/test_a.py::test_one"] == 0.5 -def test_parse_timing_data_text_format(tmp_path): +def test_parse_timing_data_text_format(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "durations.txt" timing_file.write_text("0.12s call tests/test_example.py::test_func\n") @@ -213,20 +214,20 @@ def test_parse_timing_data_text_format(tmp_path): assert result["tests/test_example.py::test_func"] == pytest.approx(0.12) -def test_parse_timing_data_none(): +def test_parse_timing_data_none() -> None: console = Console() result = parse_timing_data(console, None) assert not result -def test_parse_timing_data_nonexistent_file(tmp_path): +def test_parse_timing_data_nonexistent_file(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "nonexistent.json" result = parse_timing_data(console, timing_file) assert not result -def test_parse_timing_data_json_decode_error_fallback(tmp_path): +def test_parse_timing_data_json_decode_error_fallback(tmp_path: Path) -> None: console = Console() # File that's not valid JSON but has valid text durations timing_file = tmp_path / "mixed.txt" @@ -238,14 +239,18 @@ def test_parse_timing_data_json_decode_error_fallback(tmp_path): assert result["tests/test_example.py::test_func"] == pytest.approx(0.15) -def test_parse_timing_data_oserror_during_text_read(tmp_path, capsys, monkeypatch): +def test_parse_timing_data_oserror_during_text_read( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: console = Console() # Create a file that will cause JSON parse error timing_file = tmp_path / "bad.txt" timing_file.write_text("not json") # Mock _load_text_durations to raise OSError - def mock_load_text(*args, **kwargs): + def mock_load_text(path: Path) -> dict[str, float]: raise OSError("Mocked error") monkeypatch.setattr( @@ -259,13 +264,17 @@ def mock_load_text(*args, **kwargs): assert "Warning: Could not parse timing data" in captured.out -def test_parse_timing_data_oserror_initial_read(tmp_path, capsys, monkeypatch): +def test_parse_timing_data_oserror_initial_read( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: console = Console() timing_file = tmp_path / "test.json" timing_file.write_text("{}") # Mock _load_json_timing to raise OSError - def mock_load_json(*args, **kwargs): + def mock_load_json(path: Path) -> dict[str, float]: raise OSError("Cannot read file") monkeypatch.setattr( diff --git a/tests/fctools/test_coverage_to_tsv_processing.py b/tests/fctools/test_coverage_to_tsv_processing.py index 2edb17479..8095a9f2d 100644 --- a/tests/fctools/test_coverage_to_tsv_processing.py +++ b/tests/fctools/test_coverage_to_tsv_processing.py @@ -5,6 +5,8 @@ # pylint: disable=missing-function-docstring +from typing import Any + import pytest from firecrown.fctools.coverage_to_tsv import ( @@ -15,7 +17,7 @@ # Tests for match_test_to_function() -def test_match_test_to_function_exact_name_match(): +def test_match_test_to_function_exact_name_match() -> None: test_name = "tests/test_module.py::test_my_function" function_name = "my_function" file_path = "src/module.py" @@ -26,7 +28,7 @@ def test_match_test_to_function_exact_name_match(): assert score >= 0.5 -def test_match_test_to_function_file_match(): +def test_match_test_to_function_file_match() -> None: test_name = "tests/test_example.py::test_something" function_name = "other_function" file_path = "src/example.py" @@ -37,7 +39,7 @@ def test_match_test_to_function_file_match(): assert score == pytest.approx(0.3) -def test_match_test_to_function_full_match(): +def test_match_test_to_function_full_match() -> None: test_name = "tests/test_utils.py::test_format_string" function_name = "format_string" file_path = "src/utils.py" @@ -51,7 +53,7 @@ def test_match_test_to_function_full_match(): assert score == 1.0 -def test_match_test_to_function_no_match(): +def test_match_test_to_function_no_match() -> None: test_name = "tests/test_other.py::test_something" function_name = "unrelated_function" file_path = "src/different.py" @@ -61,7 +63,7 @@ def test_match_test_to_function_no_match(): assert score == 0.0 -def test_match_test_to_function_test_name_no_separator(): +def test_match_test_to_function_test_name_no_separator() -> None: test_name = "tests/test_module.py" function_name = "some_function" file_path = "src/module.py" @@ -76,7 +78,7 @@ def test_match_test_to_function_test_name_no_separator(): assert score >= 0.3 -def test_match_test_to_function_case_insensitive(): +def test_match_test_to_function_case_insensitive() -> None: test_name = "tests/test_module.py::test_MyFunction" function_name = "myfunction" file_path = "src/other.py" @@ -90,8 +92,8 @@ def test_match_test_to_function_case_insensitive(): # Tests for extract_coverage_data() -def test_extract_coverage_data_basic(): - coverage_data = { +def test_extract_coverage_data_basic() -> None: + coverage_data: dict[str, Any] = { "files": { "src/example.py": { "summary": { @@ -141,8 +143,8 @@ def test_extract_coverage_data_basic(): assert record.test_duration is None -def test_extract_coverage_data_with_timing(): - coverage_data = { +def test_extract_coverage_data_with_timing() -> None: + coverage_data: dict[str, Any] = { "files": { "src/utils.py": { "summary": { @@ -183,8 +185,8 @@ def test_extract_coverage_data_with_timing(): assert record.test_duration == 0.25 -def test_extract_coverage_data_timing_below_threshold(): - coverage_data = { +def test_extract_coverage_data_timing_below_threshold() -> None: + coverage_data: dict[str, Any] = { "files": { "src/module.py": { "summary": { @@ -224,8 +226,8 @@ def test_extract_coverage_data_timing_below_threshold(): assert record.test_duration is None -def test_extract_coverage_data_multiple_functions(): - coverage_data = { +def test_extract_coverage_data_multiple_functions() -> None: + coverage_data: dict[str, Any] = { "files": { "src/module.py": { "summary": { @@ -274,8 +276,8 @@ def test_extract_coverage_data_multiple_functions(): assert func_names == {"func_a", "func_b"} -def test_extract_coverage_data_no_functions(): - coverage_data = { +def test_extract_coverage_data_no_functions() -> None: + coverage_data: dict[str, Any] = { "files": { "src/empty.py": { "summary": { @@ -292,16 +294,16 @@ def test_extract_coverage_data_no_functions(): assert len(result) == 0 -def test_extract_coverage_data_empty_files(): - coverage_data: dict[str, dict[str, object]] = {"files": {}} +def test_extract_coverage_data_empty_files() -> None: + coverage_data: dict[str, Any] = {"files": {}} result = extract_coverage_data(coverage_data) assert len(result) == 0 -def test_extract_coverage_data_missing_summary_fields(): - coverage_data: dict[str, dict[str, object]] = { +def test_extract_coverage_data_missing_summary_fields() -> None: + coverage_data: dict[str, Any] = { "files": { "src/partial.py": { "summary": {}, # Missing all fields diff --git a/tests/fctools/test_coverage_to_tsv_timing.py b/tests/fctools/test_coverage_to_tsv_timing.py index c1002e44b..95b6703da 100644 --- a/tests/fctools/test_coverage_to_tsv_timing.py +++ b/tests/fctools/test_coverage_to_tsv_timing.py @@ -7,6 +7,7 @@ # pylint: disable=missing-function-docstring import json +from pathlib import Path import pytest from rich.console import Console @@ -21,7 +22,7 @@ # Tests for _parse_duration_line() -def test_parse_duration_line_valid(): +def test_parse_duration_line_valid() -> None: line = "0.12s call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -30,7 +31,7 @@ def test_parse_duration_line_valid(): assert duration == 0.12 -def test_parse_duration_line_with_setup(): +def test_parse_duration_line_with_setup() -> None: line = "0.05s setup tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -39,7 +40,7 @@ def test_parse_duration_line_with_setup(): assert duration == 0.05 -def test_parse_duration_line_with_teardown(): +def test_parse_duration_line_with_teardown() -> None: line = "0.03s teardown tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -48,7 +49,7 @@ def test_parse_duration_line_with_teardown(): assert duration == 0.03 -def test_parse_duration_line_integer_duration(): +def test_parse_duration_line_integer_duration() -> None: line = "1s call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -57,7 +58,7 @@ def test_parse_duration_line_integer_duration(): assert duration == 1.0 -def test_parse_duration_line_no_phase(): +def test_parse_duration_line_no_phase() -> None: line = "0.25s tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is not None @@ -66,19 +67,19 @@ def test_parse_duration_line_no_phase(): assert duration == 0.25 -def test_parse_duration_line_invalid(): +def test_parse_duration_line_invalid() -> None: line = "This is not a valid duration line" result = _parse_duration_line(line) assert result is None -def test_parse_duration_line_no_duration(): +def test_parse_duration_line_no_duration() -> None: line = "tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is None -def test_parse_duration_line_invalid_duration_format(): +def test_parse_duration_line_invalid_duration_format() -> None: line = "abc call tests/test_example.py::test_function" result = _parse_duration_line(line) assert result is None @@ -87,7 +88,7 @@ def test_parse_duration_line_invalid_duration_format(): # Tests for _load_text_durations() -def test_load_text_durations(tmp_path): +def test_load_text_durations(tmp_path: Path) -> None: timing_file = tmp_path / "durations.txt" timing_file.write_text( "0.12s call tests/test_example.py::test_func1\n" @@ -107,7 +108,7 @@ def test_load_text_durations(tmp_path): assert result["tests/test_example.py::test_func2"] == pytest.approx(0.08) -def test_load_text_durations_empty_file(tmp_path): +def test_load_text_durations_empty_file(tmp_path: Path) -> None: timing_file = tmp_path / "empty.txt" timing_file.write_text("") @@ -115,7 +116,7 @@ def test_load_text_durations_empty_file(tmp_path): assert not result -def test_load_text_durations_all_invalid(tmp_path): +def test_load_text_durations_all_invalid(tmp_path: Path) -> None: timing_file = tmp_path / "invalid.txt" timing_file.write_text("Invalid line 1\nInvalid line 2\nInvalid line 3\n") @@ -126,7 +127,7 @@ def test_load_text_durations_all_invalid(tmp_path): # Tests for _load_json_timing() -def test_load_json_timing(tmp_path): +def test_load_json_timing(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -144,7 +145,7 @@ def test_load_json_timing(tmp_path): assert result["tests/test_b.py::test_three"] == 0.3 -def test_load_json_timing_missing_fields(tmp_path): +def test_load_json_timing_missing_fields(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -162,7 +163,7 @@ def test_load_json_timing_missing_fields(tmp_path): assert result["tests/test_b.py::test_two"] == 0.0 -def test_load_json_timing_invalid_duration(tmp_path): +def test_load_json_timing_invalid_duration(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = { "tests": [ @@ -176,7 +177,7 @@ def test_load_json_timing_invalid_duration(tmp_path): assert result["tests/test_a.py::test_one"] == 0.0 -def test_load_json_timing_no_tests_key(tmp_path): +def test_load_json_timing_no_tests_key(tmp_path: Path) -> None: timing_file = tmp_path / "timing.json" data = {"summary": "some data", "other_field": []} timing_file.write_text(json.dumps(data)) @@ -189,7 +190,7 @@ def test_load_json_timing_no_tests_key(tmp_path): # Tests for parse_timing_data() -def test_parse_timing_data_json_format(tmp_path): +def test_parse_timing_data_json_format(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "timing.json" data = { @@ -204,7 +205,7 @@ def test_parse_timing_data_json_format(tmp_path): assert result["tests/test_a.py::test_one"] == 0.5 -def test_parse_timing_data_text_format(tmp_path): +def test_parse_timing_data_text_format(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "durations.txt" timing_file.write_text("0.12s call tests/test_example.py::test_func\n") @@ -214,20 +215,20 @@ def test_parse_timing_data_text_format(tmp_path): assert result["tests/test_example.py::test_func"] == pytest.approx(0.12) -def test_parse_timing_data_none(): +def test_parse_timing_data_none() -> None: console = Console() result = parse_timing_data(console, None) assert not result -def test_parse_timing_data_nonexistent_file(tmp_path): +def test_parse_timing_data_nonexistent_file(tmp_path: Path) -> None: console = Console() timing_file = tmp_path / "nonexistent.json" result = parse_timing_data(console, timing_file) assert not result -def test_parse_timing_data_json_decode_error_fallback(tmp_path): +def test_parse_timing_data_json_decode_error_fallback(tmp_path: Path) -> None: console = Console() # File that's not valid JSON but has valid text durations timing_file = tmp_path / "mixed.txt" @@ -239,14 +240,18 @@ def test_parse_timing_data_json_decode_error_fallback(tmp_path): assert result["tests/test_example.py::test_func"] == pytest.approx(0.15) -def test_parse_timing_data_oserror_during_text_read(tmp_path, capsys, monkeypatch): +def test_parse_timing_data_oserror_during_text_read( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: console = Console() # Create a file that will cause JSON parse error timing_file = tmp_path / "bad.txt" timing_file.write_text("not json") # Mock _load_text_durations to raise OSError - def mock_load_text(*args, **kwargs): + def mock_load_text(path: Path) -> dict[str, float]: raise OSError("Mocked error") monkeypatch.setattr( diff --git a/tests/fctools/test_generate_symbol_map.py b/tests/fctools/test_generate_symbol_map.py index f8575bbda..09792b0a8 100644 --- a/tests/fctools/test_generate_symbol_map.py +++ b/tests/fctools/test_generate_symbol_map.py @@ -5,6 +5,7 @@ """ import json +from pathlib import Path from types import ModuleType import pytest @@ -36,14 +37,14 @@ class MockExternalClass: __module__ = "external.package" -def mock_function(): +def mock_function() -> None: """Mock function for testing.""" mock_function.__module__ = "firecrown.mock.module" -def test_is_private_name(): +def test_is_private_name() -> None: """Test that private names are detected.""" assert _is_private_name("_private") assert _is_private_name("__dunder__") @@ -51,23 +52,23 @@ def test_is_private_name(): assert not _is_private_name("MY_CONSTANT") -def test_is_excluded_type_class(): +def test_is_excluded_type_class() -> None: """Test that classes are identified as excluded types.""" assert _is_excluded_type(MockFirecrownClass) -def test_is_excluded_type_function(): +def test_is_excluded_type_function() -> None: """Test that functions are identified as excluded types.""" assert _is_excluded_type(mock_function) -def test_is_excluded_type_module(): +def test_is_excluded_type_module() -> None: """Test that modules are identified as excluded types.""" mock_module = ModuleType("mock") assert _is_excluded_type(mock_module) -def test_is_excluded_type_instance(): +def test_is_excluded_type_instance() -> None: """Test that instances are not excluded types.""" instance = MockFirecrownClass() assert not _is_excluded_type(instance) @@ -75,7 +76,7 @@ def test_is_excluded_type_instance(): assert not _is_excluded_type("value") -def test_has_constant_name_uppercase(): +def test_has_constant_name_uppercase() -> None: """Test that UPPER_CASE names are recognized.""" assert _has_constant_name("CONSTANT") assert _has_constant_name("MY_CONSTANT") @@ -84,7 +85,7 @@ def test_has_constant_name_uppercase(): assert _has_constant_name("FOUND4HELP") -def test_has_constant_name_mixed_case(): +def test_has_constant_name_mixed_case() -> None: """Test that Mixed_Case names are NOT recognized. The regex pattern requires all-uppercase constant names. @@ -93,26 +94,26 @@ def test_has_constant_name_mixed_case(): assert not _has_constant_name("Some_Constant") -def test_has_constant_name_lowercase(): +def test_has_constant_name_lowercase() -> None: """Test that lowercase names are not recognized.""" assert not _has_constant_name("lowercase") assert not _has_constant_name("my_var") assert not _has_constant_name("some_variable") -def test_is_firecrown_instance_with_firecrown_class(): +def test_is_firecrown_instance_with_firecrown_class() -> None: """Test that instances of Firecrown classes are identified.""" instance = MockFirecrownClass() assert _is_firecrown_instance(instance) -def test_is_firecrown_instance_with_external_class(): +def test_is_firecrown_instance_with_external_class() -> None: """Test that instances of external classes are not identified.""" instance = MockExternalClass() assert not _is_firecrown_instance(instance) -def test_is_firecrown_instance_with_builtin(): +def test_is_firecrown_instance_with_builtin() -> None: """Test that builtin instances are not identified as Firecrown.""" assert not _is_firecrown_instance(42) assert not _is_firecrown_instance("value") @@ -120,36 +121,36 @@ def test_is_firecrown_instance_with_builtin(): assert not _is_firecrown_instance({"key": "value"}) -def test_is_api_constant_private_name(): +def test_is_api_constant_private_name() -> None: """Test that private names are rejected.""" assert not _is_api_constant("_private", "anything") assert not _is_api_constant("__dunder__", "anything") -def test_is_api_constant_class(): +def test_is_api_constant_class() -> None: """Test that classes are rejected (handled separately).""" assert not _is_api_constant("SomeClass", MockFirecrownClass) -def test_is_api_constant_function(): +def test_is_api_constant_function() -> None: """Test that functions are rejected (handled separately).""" assert not _is_api_constant("some_function", mock_function) -def test_is_api_constant_module(): +def test_is_api_constant_module() -> None: """Test that modules are rejected.""" mock_module = ModuleType("mock") assert not _is_api_constant("some_module", mock_module) -def test_is_api_constant_uppercase(): +def test_is_api_constant_uppercase() -> None: """Test that UPPER_CASE names are accepted.""" assert _is_api_constant("CONSTANT", 42) assert _is_api_constant("MY_CONSTANT", "value") assert _is_api_constant("API_KEY", [1, 2, 3]) -def test_is_api_constant_mixed_case_with_underscore(): +def test_is_api_constant_mixed_case_with_underscore() -> None: """Test that Mixed_Case_With_Underscores names are NOT accepted. The regex pattern requires all-uppercase constant names. @@ -161,25 +162,25 @@ def test_is_api_constant_mixed_case_with_underscore(): assert _is_api_constant("Y1_LENS_BINS", {"bin1": 0.1}) -def test_is_api_constant_lowercase(): +def test_is_api_constant_lowercase() -> None: """Test that lowercase names are rejected.""" assert not _is_api_constant("lowercase", "value") assert not _is_api_constant("my_var", 123) -def test_is_api_constant_firecrown_instance(): +def test_is_api_constant_firecrown_instance() -> None: """Test that instances of Firecrown classes are accepted.""" instance = MockFirecrownClass() assert _is_api_constant("SomeInstance", instance) -def test_is_api_constant_external_instance(): +def test_is_api_constant_external_instance() -> None: """Test that instances of external classes are rejected.""" instance = MockExternalClass() assert not _is_api_constant("ExternalInstance", instance) -def test_add_symbol_to_map_class(): +def test_add_symbol_to_map_class() -> None: """Test adding a class to the symbol map.""" symbols: dict[str, str] = {} _add_symbol_to_map( @@ -193,7 +194,7 @@ def test_add_symbol_to_map_class(): ) -def test_add_symbol_to_map_function(): +def test_add_symbol_to_map_function() -> None: """Test adding a function to the symbol map.""" symbols: dict[str, str] = {} _add_symbol_to_map(symbols, "test.module", "test_func", mock_function, "firecrown") @@ -205,7 +206,7 @@ def test_add_symbol_to_map_function(): ) -def test_add_symbol_to_map_re_exported_class(): +def test_add_symbol_to_map_re_exported_class() -> None: """Test that re-exported classes get both public and private paths.""" symbols: dict[str, str] = {} @@ -221,7 +222,7 @@ def test_add_symbol_to_map_re_exported_class(): assert "firecrown.mock.module.MyClass" in symbols -def test_add_symbol_to_map_external_class(): +def test_add_symbol_to_map_external_class() -> None: """Test that external classes are not added.""" symbols: dict[str, str] = {} _add_symbol_to_map( @@ -231,7 +232,7 @@ def test_add_symbol_to_map_external_class(): assert len(symbols) == 0 -def test_add_symbol_to_map_constant(): +def test_add_symbol_to_map_constant() -> None: """Test adding a constant to the symbol map.""" symbols: dict[str, str] = {} my_constant = {"key": "value"} @@ -243,7 +244,7 @@ def test_add_symbol_to_map_constant(): assert symbols["test.module.MY_CONSTANT"] == expected_url -def test_add_symbol_to_map_mixed_case_constant(): +def test_add_symbol_to_map_mixed_case_constant() -> None: """Test adding a Mixed_Case constant to the symbol map.""" symbols: dict[str, str] = {} bins = {"bin1": 0.1, "bin2": 0.2} @@ -253,7 +254,7 @@ def test_add_symbol_to_map_mixed_case_constant(): assert "test.module.Y1_LENS_BINS" in symbols -def test_add_symbol_to_map_firecrown_instance(): +def test_add_symbol_to_map_firecrown_instance() -> None: """Test adding a Firecrown class instance to the symbol map.""" symbols: dict[str, str] = {} instance = MockFirecrownClass() @@ -263,7 +264,7 @@ def test_add_symbol_to_map_firecrown_instance(): assert "test.module.INSTANCE" in symbols -def test_add_symbol_to_map_lowercase_variable(): +def test_add_symbol_to_map_lowercase_variable() -> None: """Test that lowercase variables are not added.""" symbols: dict[str, str] = {} _add_symbol_to_map(symbols, "test.module", "lowercase_var", 42, "firecrown") @@ -271,7 +272,7 @@ def test_add_symbol_to_map_lowercase_variable(): assert len(symbols) == 0 -def test_add_symbol_to_map_private_constant(): +def test_add_symbol_to_map_private_constant() -> None: """Test that private constants are not added.""" symbols: dict[str, str] = {} _add_symbol_to_map(symbols, "test.module", "_PRIVATE_CONST", 42, "firecrown") @@ -279,7 +280,7 @@ def test_add_symbol_to_map_private_constant(): assert len(symbols) == 0 -def test_get_all_symbols_with_mock_package(): +def test_get_all_symbols_with_mock_package() -> None: """Test get_all_symbols with a mock package structure.""" # Create a minimal mock package mock_package = ModuleType("mock_package") @@ -290,20 +291,20 @@ def test_get_all_symbols_with_mock_package(): original_walk = pkgutil.walk_packages - def mock_walk(*_args, **_kwargs): + def mock_walk(*_args: object, **_kwargs: object) -> list[tuple[object, str, bool]]: return [] - pkgutil.walk_packages = mock_walk + setattr(pkgutil, "walk_packages", mock_walk) try: symbols = get_all_symbols(mock_package) # Should at least handle the empty case without crashing assert isinstance(symbols, dict) finally: - pkgutil.walk_packages = original_walk + setattr(pkgutil, "walk_packages", original_walk) -def test_get_all_symbols_real_firecrown_subset(): +def test_get_all_symbols_real_firecrown_subset() -> None: """Test get_all_symbols with real firecrown.updatable module.""" import firecrown.updatable # pylint: disable=import-outside-toplevel @@ -318,7 +319,7 @@ def test_get_all_symbols_real_firecrown_subset(): assert len(param_symbols) > 0 -def test_get_all_symbols_captures_classes(): +def test_get_all_symbols_captures_classes() -> None: """Test that get_all_symbols captures class definitions.""" import firecrown.updatable # pylint: disable=import-outside-toplevel @@ -333,7 +334,7 @@ def test_get_all_symbols_captures_classes(): assert len(classes) > 0 -def test_get_all_symbols_captures_functions(): +def test_get_all_symbols_captures_functions() -> None: """Test that get_all_symbols captures function definitions.""" import firecrown.updatable # pylint: disable=import-outside-toplevel @@ -343,7 +344,7 @@ def test_get_all_symbols_captures_functions(): assert len(symbols) > 0 -def test_get_all_symbols_excludes_private(): +def test_get_all_symbols_excludes_private() -> None: """Test that get_all_symbols excludes private members. The function excludes members with names starting with underscore, @@ -365,7 +366,7 @@ def test_get_all_symbols_excludes_private(): assert not after_derived.startswith("_"), f"Found private member: {sym}" -def test_get_all_symbols_with_generators(): +def test_get_all_symbols_with_generators() -> None: """Test get_all_symbols captures constants from generators.""" import firecrown.generators # pylint: disable=import-outside-toplevel @@ -397,7 +398,7 @@ def test_get_all_symbols_with_generators(): @pytest.mark.filterwarnings( r"ignore:.*firecrown\.parameters module is deprecated.*:DeprecationWarning" ) -def test_main_outputs_valid_json(): +def test_main_outputs_valid_json() -> None: """Test that main command outputs valid JSON to stdout.""" result = runner.invoke(app, []) assert result.exit_code == 0 @@ -413,7 +414,7 @@ def test_main_outputs_valid_json(): assert any("firecrown" in key for key in data) -def test_main_with_output_file(tmp_path): +def test_main_with_output_file(tmp_path: Path) -> None: """Test main command with --output option writes to file.""" output_file = tmp_path / "symbols.json" @@ -430,7 +431,7 @@ def test_main_with_output_file(tmp_path): assert len(data) > 0 -def test_main_with_compact_format(tmp_path): +def test_main_with_compact_format(tmp_path: Path) -> None: """Test --compact flag produces single-line JSON.""" output_file = tmp_path / "compact.json" @@ -448,7 +449,7 @@ def test_main_with_compact_format(tmp_path): assert len(data) > 0 -def test_main_default_is_pretty(tmp_path): +def test_main_default_is_pretty(tmp_path: Path) -> None: """Test that default formatting is pretty (multi-line).""" output_file = tmp_path / "pretty.json" @@ -462,7 +463,7 @@ def test_main_default_is_pretty(tmp_path): assert content.count("\n") > 10 -def test_main_symbol_count(): +def test_main_symbol_count() -> None: """Test that main command reports symbol count.""" result = runner.invoke(app, []) assert result.exit_code == 0 @@ -475,7 +476,7 @@ def test_main_symbol_count(): assert symbol_count > 50 -def test_main_output_with_status_message(tmp_path): +def test_main_output_with_status_message(tmp_path: Path) -> None: """Test that main command shows status message when writing to file.""" output_file = tmp_path / "symbols.json" @@ -487,7 +488,7 @@ def test_main_output_with_status_message(tmp_path): assert "symbols" in result.stderr -def test_is_api_constant_with_non_firecrown_class(): +def test_is_api_constant_with_non_firecrown_class() -> None: """Test _is_api_constant with object that has class with non-firecrown module.""" # Create an object with a class that has __module__ but not from firecrown obj = object() # object's __class__.__module__ is 'builtins' @@ -498,7 +499,7 @@ def test_is_api_constant_with_non_firecrown_class(): assert result is False -def test_is_api_constant_mixed_case_only_second_condition(): +def test_is_api_constant_mixed_case_only_second_condition() -> None: """Test _is_api_constant with various naming patterns. The regex pattern now requires proper UPPER_CASE constant naming. diff --git a/tests/fctools/test_link_checker.py b/tests/fctools/test_link_checker.py index d3b64ad54..44d276bbf 100644 --- a/tests/fctools/test_link_checker.py +++ b/tests/fctools/test_link_checker.py @@ -10,7 +10,7 @@ import io from contextlib import redirect_stdout from pathlib import Path -from typing import cast +from typing import Callable, NoReturn, Protocol, cast import pytest import requests @@ -21,6 +21,17 @@ from firecrown.fctools import link_checker +class RateLimitedResponse(Protocol): + """Response members read by the link checker's HTTP 429 handling.""" + + status_code: int + reason: str + + def raise_for_status(self) -> None: + """Raise for an unsuccessful response.""" + ... # pylint: disable=unnecessary-ellipsis + + @pytest.fixture(name="site_dir") def fixture_site_dir(tmp_path: Path) -> Path: """Provide a temporary directory to be used as the site root for tests. @@ -53,7 +64,9 @@ def fixture_sample_site(site_dir: Path) -> Path: @pytest.fixture(name="mock_requests_ok") -def fixture_mock_requests_ok(monkeypatch): +def fixture_mock_requests_ok( + monkeypatch: pytest.MonkeyPatch, +) -> Callable[..., object]: """Mock ``requests.Session.get``. Mock to return a simple HTML page containing id 'extid'. @@ -62,15 +75,20 @@ def fixture_mock_requests_ok(monkeypatch): class FakeResp: """Fake response object for requests.get().""" - def __init__(self, content: str): + def __init__(self, content: str) -> None: """Initialize with given content string.""" self.content = content.encode("utf-8") - def raise_for_status(self): + def raise_for_status(self) -> None: """Simulate a successful response.""" return None - def _fake_get(_self, _url, timeout=None, **_kwargs): + def _fake_get( + _self: requests.Session, + _url: str, + timeout: int | None = None, + **_kwargs: object, + ) -> FakeResp: assert timeout is not None return FakeResp('
') @@ -78,18 +96,18 @@ def _fake_get(_self, _url, timeout=None, **_kwargs): return _fake_get -def make_429_response(_: str): +def make_429_response(_: str) -> RateLimitedResponse: """Factory to create a mock response that raises HTTPError for 429.""" class Fake429Resp: """Fake response object for requests.get() that returns 429.""" - def __init__(self): + def __init__(self) -> None: """Create fake response.""" self.status_code = 429 self.reason = "Too Many Requests" - def raise_for_status(self): + def raise_for_status(self) -> None: """Simulate HTTP 429 error.""" http_error = requests.HTTPError("429 Client Error: Too Many Requests") # Test double: only status_code is read by the code under test. @@ -99,7 +117,7 @@ def raise_for_status(self): return Fake429Resp() -def test_extract_ids_basic(site_dir: Path): +def test_extract_ids_basic(site_dir: Path) -> None: """Test extraction of ids from a simple HTML file.""" html = site_dir / "page.html" html.write_text( @@ -115,7 +133,7 @@ def test_extract_ids_basic(site_dir: Path): assert ids == {"one", "two"} -def test_check_anchors_local_ok(site_dir: Path): +def test_check_anchors_local_ok(site_dir: Path) -> None: # target page with an id b = site_dir / "b.html" b.write_text("

Section

") @@ -140,7 +158,9 @@ def test_check_anchors_local_ok(site_dir: Path): assert sc.invalid_anchors == 0 -def test_check_anchors_missing_anchor_returns_failure_and_main_exit(site_dir: Path): +def test_check_anchors_missing_anchor_returns_failure_and_main_exit( + site_dir: Path, +) -> None: # target without the requested id b = site_dir / "b.html" b.write_text("

Section

") @@ -155,7 +175,11 @@ def test_check_anchors_missing_anchor_returns_failure_and_main_exit(site_dir: Pa assert code != 0 -def test_main_uses_full_broken_link_output_in_ci(site_dir: Path, monkeypatch, capsys): +def test_main_uses_full_broken_link_output_in_ci( + site_dir: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: """CI output should include complete source and target values.""" monkeypatch.setenv("CI", "true") source = site_dir / "a-very-long-source-file-name.html" @@ -174,7 +198,11 @@ def test_main_uses_full_broken_link_output_in_ci(site_dir: Path, monkeypatch, ca assert "Reason: file missing" in captured.out -def test_main_can_use_table_output_in_ci(site_dir: Path, monkeypatch, capsys): +def test_main_can_use_table_output_in_ci( + site_dir: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: """The table output remains available when explicitly requested.""" monkeypatch.setenv("CI", "true") (site_dir / "source.html").write_text('broken') @@ -194,14 +222,16 @@ def test_main_can_use_table_output_in_ci(site_dir: Path, monkeypatch, capsys): def test_skip_external_prevents_download_and_allows_success( - site_dir: Path, monkeypatch -): + site_dir: Path, monkeypatch: pytest.MonkeyPatch +) -> None: # source linking to an external URL with fragment a = site_dir / "a.html" a.write_text('ext') # Simulate requests raising if called (network would fail) - def _raise_get(self, url, timeout=None): + def _raise_get( + self: requests.Session, url: str, timeout: object = None + ) -> NoReturn: raise requests.RequestException("no network in tests") monkeypatch.setattr(requests.Session, "get", _raise_get) @@ -242,7 +272,7 @@ def _raise_get(self, url, timeout=None): ) def test_parametrized_link_scenarios( sample_site: Path, fname: str, content: str, expected: int -): +) -> None: (sample_site / fname).write_text(content) code = link_checker.main( sample_site, download_timeout=1, verbose=False, skip_external=True @@ -250,7 +280,9 @@ def test_parametrized_link_scenarios( assert code == expected -def test_external_download_with_mock(sample_site: Path, mock_requests_ok): +def test_external_download_with_mock( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: # external link to page with id extid; mocked response supplies that id assert mock_requests_ok is not None # to avoid unused variable warning (sample_site / "ext.html").write_text( @@ -262,12 +294,14 @@ def test_external_download_with_mock(sample_site: Path, mock_requests_ok): assert code == 0 -def test_external_download_fails_when_get_raises(sample_site: Path, monkeypatch): +def test_external_download_fails_when_get_raises( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: (sample_site / "ext2.html").write_text( 'ext' ) - def _raise(self, url, timeout=None): + def _raise(self: requests.Session, url: str, timeout: object = None) -> NoReturn: raise requests.RequestException("simulated network error") monkeypatch.setattr(requests.Session, "get", _raise) @@ -277,7 +311,9 @@ def _raise(self, url, timeout=None): assert code != 0 -def test_download_url_writes_file_when_ok(sample_site: Path, mock_requests_ok): +def test_download_url_writes_file_when_ok( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning console = Console(record=True) sc = link_checker.SiteChecker( @@ -306,14 +342,26 @@ def test_download_url_writes_file_when_ok(sample_site: Path, mock_requests_ok): ], ) def test_download_url_handles_failure_and_returns_path( - sample_site: Path, monkeypatch, exc_type, exc_msg, status_code -): + sample_site: Path, + monkeypatch: pytest.MonkeyPatch, + exc_type: type[requests.RequestException], + exc_msg: str, + status_code: int | None, +) -> None: console = Console(record=True) - def _raise(_self, _url, _timeout=None, **_kwargs): + def _raise( + _self: requests.Session, + _url: str, + _timeout: object = None, + **_kwargs: object, + ) -> NoReturn: if status_code is not None: exc = exc_type(exc_msg) - exc.response = type("FakeResp", (), {"status_code": status_code})() + # Only status_code is read from this deliberately minimal response. + exc.response = cast( + Response, type("FakeResp", (), {"status_code": status_code})() + ) else: exc = exc_type(exc_msg) raise exc @@ -339,11 +387,15 @@ def _raise(_self, _url, _timeout=None, **_kwargs): sc.close() -def test_download_url_records_429_error(sample_site: Path, monkeypatch): +def test_download_url_records_429_error( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that _download_url records 429 errors in rate_limited_urls dict.""" console = Console(record=True) - def _raise_429(self, url, _=None, **_kwargs): + def _raise_429( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse: assert self is not None # to silence pylint resp = make_429_response(url) return resp @@ -376,13 +428,17 @@ def _raise_429(self, url, _=None, **_kwargs): sc.close() -def test_rate_limited_links_dont_count_as_invalid(sample_site: Path, monkeypatch): +def test_rate_limited_links_dont_count_as_invalid( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that 429 rate-limited links don't increment invalid_links counter.""" (sample_site / "link.html").write_text( 'wiki' ) - def _raise_429(self, url, _=None, **_kwargs): + def _raise_429( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse: assert self is not None # to silence pylint resp = make_429_response(url) return resp @@ -410,13 +466,17 @@ def _raise_429(self, url, _=None, **_kwargs): assert any("en.wikipedia.org" in u for u in sc.rate_limited_urls) -def test_main_returns_zero_for_429_failures_only(site_dir: Path, monkeypatch): +def test_main_returns_zero_for_429_failures_only( + site_dir: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that main() returns 0 when only 429 failures occur (not broken links).""" (site_dir / "link.html").write_text( 'wiki' ) - def _raise_429(self, url, _=None, **_kwargs): + def _raise_429( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse: assert self is not None # to silence pylint resp = make_429_response(url) return resp @@ -430,7 +490,9 @@ def _raise_429(self, url, _=None, **_kwargs): assert code == 0 -def test_mixed_429_and_real_failure_uses_404_exit_code(site_dir: Path, monkeypatch): +def test_mixed_429_and_real_failure_uses_404_exit_code( + site_dir: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that a mix of 429 and real failures uses the real failure exit code.""" # Create a real broken link (missing file) (site_dir / "link.html").write_text('broken') @@ -439,7 +501,9 @@ def test_mixed_429_and_real_failure_uses_404_exit_code(site_dir: Path, monkeypat 'wiki' ) - def _raise_or_404(self, url, _=None, **_kwargs): + def _raise_or_404( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse | None: assert self is not None # to silence pylint if "en.wikipedia.org" in url: resp = make_429_response(url) @@ -456,13 +520,17 @@ def _raise_or_404(self, url, _=None, **_kwargs): assert code != 0 -def test_rate_limited_urls_appear_in_report(sample_site: Path, monkeypatch): +def test_rate_limited_urls_appear_in_report( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that rate-limited URLs appear in verbose output.""" (sample_site / "link.html").write_text( 'wiki' ) - def _raise_429(self, url, _=None, **_kwargs): + def _raise_429( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse: assert self is not None # to silence pylint resp = make_429_response(url) return resp @@ -486,7 +554,9 @@ def _raise_429(self, url, _=None, **_kwargs): assert "wikipedia" in out.lower() -def test_429_responses_are_cached_like_other_failures(sample_site: Path, monkeypatch): +def test_429_responses_are_cached_like_other_failures( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that 429 responses get cached like other failed downloads.""" (sample_site / "link.html").write_text( 'wiki' @@ -494,7 +564,9 @@ def test_429_responses_are_cached_like_other_failures(sample_site: Path, monkeyp call_count = 0 - def _raise_429_count(self, url, _=None, **_kwargs): + def _raise_429_count( + self: requests.Session, url: str, _: object = None, **_kwargs: object + ) -> RateLimitedResponse: assert self is not None # to silence pylint nonlocal call_count call_count += 1 @@ -528,7 +600,7 @@ def _raise_429_count(self, url, _=None, **_kwargs): assert "http://en.wikipedia.org/wiki/Abstract_type" in sc.downloaded_files -def test_normalize_fragment_only(sample_site: Path): +def test_normalize_fragment_only(sample_site: Path) -> None: console = Console() sc = link_checker.SiteChecker( sample_site, @@ -549,7 +621,7 @@ def test_normalize_fragment_only(sample_site: Path): sc.close() -def test_missing_file_reports_file_missing(sample_site: Path): +def test_missing_file_reports_file_missing(sample_site: Path) -> None: # source linking to a missing file (sample_site / "src.html").write_text('bad') console = Console() @@ -569,13 +641,17 @@ def test_missing_file_reports_file_missing(sample_site: Path): assert any(r == "file missing" for _, _, r in missing) -def test_unreachable_external_is_reported(sample_site: Path, monkeypatch): +def test_unreachable_external_is_reported( + sample_site: Path, monkeypatch: pytest.MonkeyPatch +) -> None: # external link with download failing (sample_site / "ext3.html").write_text( 'ext' ) - def _raise(self, url, timeout=None, **_kwargs): + def _raise( + self: requests.Session, url: str, timeout: object = None, **_kwargs: object + ) -> NoReturn: raise requests.RequestException("boom") monkeypatch.setattr(requests.Session, "get", _raise) @@ -598,7 +674,7 @@ def _raise(self, url, timeout=None, **_kwargs): assert any(r == "unreachable link" for _, _, r in missing) -def test_ignore_non_html_files(sample_site: Path): +def test_ignore_non_html_files(sample_site: Path) -> None: # create a non-HTML file with a link (sample_site / "file.txt").write_text('ok') @@ -624,8 +700,8 @@ def test_ignore_non_html_files(sample_site: Path): def test_downloading_same_url_multiple_times_uses_cache( - sample_site: Path, mock_requests_ok -): + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning console = Console(record=True) sc = link_checker.SiteChecker( @@ -654,8 +730,8 @@ def test_downloading_same_url_multiple_times_uses_cache( def test_normalize_href_with_url_multiple_times_uses_cache( - sample_site: Path, mock_requests_ok -): + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning console = Console(record=True) sc = link_checker.SiteChecker( @@ -685,7 +761,9 @@ def test_normalize_href_with_url_multiple_times_uses_cache( sc.close() -def test_page_with_id_with_no_value(sample_site: Path, mock_requests_ok): +def test_page_with_id_with_no_value( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with an element that has an id attribute with no value (sample_site / "idless.html").write_text("
") @@ -711,7 +789,9 @@ def test_page_with_id_with_no_value(sample_site: Path, mock_requests_ok): assert sc.invalid_anchors == 0 -def test_page_with_name_with_no_value(sample_site: Path, mock_requests_ok): +def test_page_with_name_with_no_value( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with an element that has a name attribute with no value (sample_site / "nameless.html").write_text("") @@ -737,7 +817,9 @@ def test_page_with_name_with_no_value(sample_site: Path, mock_requests_ok): assert sc.invalid_anchors == 0 -def test_page_with_href_with_no_value(sample_site: Path, mock_requests_ok): +def test_page_with_href_with_no_value( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with a link that has an href attribute with no value (sample_site / "hrefless.html").write_text("") @@ -763,7 +845,9 @@ def test_page_with_href_with_no_value(sample_site: Path, mock_requests_ok): assert sc.invalid_anchors == 0 -def test_skipping_external_links_verbose_output(sample_site: Path, mock_requests_ok): +def test_skipping_external_links_verbose_output( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with an external link (sample_site / "external.html").write_text( @@ -790,7 +874,9 @@ def test_skipping_external_links_verbose_output(sample_site: Path, mock_requests assert "Skipping external link" in out -def test_page_with_multiple_ids(sample_site: Path, mock_requests_ok): +def test_page_with_multiple_ids( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with multiple elements having ids (sample_site / "multiid.html").write_text( @@ -824,8 +910,8 @@ def test_page_with_multiple_ids(sample_site: Path, mock_requests_ok): def test_page_with_links_with_same_url_and_different_fragments( - sample_site: Path, mock_requests_ok -): + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with multiple links to the same URL but different fragments (sample_site / "links.html").write_text( @@ -861,8 +947,8 @@ def test_page_with_links_with_same_url_and_different_fragments( def test_link_without_fragment_is_handled_correctly( - sample_site: Path, mock_requests_ok -): + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning # Create a page with a link without a fragment (sample_site / "nolinkfrag.html").write_text( @@ -890,7 +976,9 @@ def test_link_without_fragment_is_handled_correctly( assert sc.invalid_anchors == 0 -def test_cli_runner_exit_codes(sample_site: Path, mock_requests_ok): +def test_cli_runner_exit_codes( + sample_site: Path, mock_requests_ok: Callable[..., object] +) -> None: assert mock_requests_ok is not None # to avoid unused variable warning runner = CliRunner() diff --git a/tests/fctools/test_list_tools.py b/tests/fctools/test_list_tools.py index 937dca51d..3134d5057 100644 --- a/tests/fctools/test_list_tools.py +++ b/tests/fctools/test_list_tools.py @@ -6,7 +6,9 @@ import subprocess import sys from pathlib import Path +from typing import NoReturn +import pytest from typer.testing import CliRunner from firecrown.fctools.list_tools import ( @@ -22,7 +24,7 @@ class TestExtractDescriptionFromDocstring: """Tests for _extract_description_from_docstring function.""" - def test_extract_from_simple_docstring(self): + def test_extract_from_simple_docstring(self) -> None: """Test extracting description from a simple docstring.""" docstring = "This is a simple tool." @@ -30,7 +32,7 @@ def test_extract_from_simple_docstring(self): assert result == "This is a simple tool." - def test_extract_from_multiline_docstring(self): + def test_extract_from_multiline_docstring(self) -> None: """Test extracting first line from multiline docstring.""" docstring = """This is the first line. @@ -41,7 +43,7 @@ def test_extract_from_multiline_docstring(self): assert result == "This is the first line." - def test_extract_with_triple_quotes(self): + def test_extract_with_triple_quotes(self) -> None: """Test handling docstring with triple quotes in text. The function filters out lines that start with triple quotes, @@ -54,7 +56,7 @@ def test_extract_with_triple_quotes(self): # Lines starting with """ are filtered out assert result == "Tool description not available" - def test_extract_from_empty_docstring(self): + def test_extract_from_empty_docstring(self) -> None: """Test handling empty docstring.""" docstring = "" @@ -62,7 +64,7 @@ def test_extract_from_empty_docstring(self): assert result == "Tool description not available" - def test_extract_from_whitespace_only(self): + def test_extract_from_whitespace_only(self) -> None: """Test handling whitespace-only docstring.""" docstring = " \n \n " @@ -70,7 +72,7 @@ def test_extract_from_whitespace_only(self): assert result == "Tool description not available" - def test_extract_skips_empty_lines(self): + def test_extract_skips_empty_lines(self) -> None: """Test that empty lines are skipped.""" docstring = """ @@ -81,7 +83,7 @@ def test_extract_skips_empty_lines(self): assert result == "This is the description." - def test_extract_with_single_quotes(self): + def test_extract_with_single_quotes(self) -> None: """Test handling docstring with single triple quotes in text. The function filters out lines that start with triple quotes, @@ -98,7 +100,7 @@ def test_extract_with_single_quotes(self): class TestExtractDescriptionFromFile: # pylint: disable=import-outside-toplevel """Tests for _extract_description_from_file function.""" - def test_extract_from_valid_python_file(self, tmp_path): + def test_extract_from_valid_python_file(self, tmp_path: Path) -> None: """Test extracting description from a valid Python file.""" test_file = tmp_path / "test_tool.py" test_file.write_text( @@ -109,7 +111,7 @@ def test_extract_from_valid_python_file(self, tmp_path): assert "test tool" in result.lower() - def test_extract_from_file_with_multiline_docstring(self, tmp_path): + def test_extract_from_file_with_multiline_docstring(self, tmp_path: Path) -> None: """Test extracting from file with multiline docstring.""" test_file = tmp_path / "tool.py" test_file.write_text( @@ -120,7 +122,7 @@ def test_extract_from_file_with_multiline_docstring(self, tmp_path): assert "First line is important" in result - def test_extract_from_file_without_docstring(self, tmp_path): + def test_extract_from_file_without_docstring(self, tmp_path: Path) -> None: """Test handling file without module docstring.""" test_file = tmp_path / "no_doc.py" test_file.write_text("def main():\n pass\n") @@ -129,7 +131,7 @@ def test_extract_from_file_without_docstring(self, tmp_path): assert result == "Tool description not available" - def test_extract_from_nonexistent_file(self, tmp_path): + def test_extract_from_nonexistent_file(self, tmp_path: Path) -> None: """Test handling nonexistent file.""" fake_file = tmp_path / "nonexistent_tool.py" # Don't create the file, just use the path @@ -138,7 +140,7 @@ def test_extract_from_nonexistent_file(self, tmp_path): assert result == "Tool description not available" - def test_extract_from_file_with_syntax_error(self, tmp_path): + def test_extract_from_file_with_syntax_error(self, tmp_path: Path) -> None: """Test handling file with syntax errors. AST parsing will fail on syntax errors, falling back to the @@ -152,7 +154,7 @@ def test_extract_from_file_with_syntax_error(self, tmp_path): # Syntax error in the code causes AST parsing to fail assert result == "Tool description not available" - def test_extract_from_file_with_invalid_unicode(self, tmp_path): + def test_extract_from_file_with_invalid_unicode(self, tmp_path: Path) -> None: """Test handling file with encoding errors.""" test_file = tmp_path / "bad_encoding.py" test_file.write_bytes(b"\xff\xfe\x00\x00") @@ -161,7 +163,7 @@ def test_extract_from_file_with_invalid_unicode(self, tmp_path): assert result == "Tool description not available" - def test_extract_with_long_docstring(self, tmp_path): + def test_extract_with_long_docstring(self, tmp_path: Path) -> None: """Test that long docstrings are truncated.""" long_desc = "A" * 200 # Longer than 80 chars test_file = tmp_path / "long_doc.py" @@ -172,14 +174,16 @@ def test_extract_with_long_docstring(self, tmp_path): # Should be truncated to around 80 chars assert len(result) <= 85 # Allow some margin for ellipsis - def test_extract_fallback_to_import(self, tmp_path, monkeypatch): + def test_extract_fallback_to_import( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: """Test fallback to importlib when AST fails.""" # Create a file with a valid docstring test_file = tmp_path / "import_test.py" test_file.write_text('"""Fallback test tool."""\nx = 1\n') # Mock get_module_docstring to raise OSError to trigger fallback - def mock_get_module_docstring(path): + def mock_get_module_docstring(path: Path) -> NoReturn: raise OSError("Simulated error") import firecrown.fctools.list_tools as list_tools_module @@ -193,14 +197,16 @@ def mock_get_module_docstring(path): # Should get description via importlib fallback assert "Fallback test tool" in result - def test_extract_fallback_with_no_spec_loader(self, tmp_path, monkeypatch): + def test_extract_fallback_with_no_spec_loader( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: """Test fallback when spec.loader is None.""" test_file = tmp_path / "no_loader.py" test_file.write_text('"""Test."""\n') # Mock to return a spec without loader # pylint: disable=unused-argument - def mock_spec_from_file_location(name, location): + def mock_spec_from_file_location(name: str, location: str) -> object: class FakeSpec: """Mock spec without loader.""" @@ -215,7 +221,7 @@ class FakeSpec: ) # Also mock get_module_docstring to fail - def mock_get_module_docstring(path): + def mock_get_module_docstring(path: Path) -> NoReturn: raise OSError("Simulated error") import firecrown.fctools.list_tools as list_tools_module @@ -229,14 +235,16 @@ def mock_get_module_docstring(path): # Should return fallback since spec.loader is None assert result == "Tool description not available" - def test_extract_fallback_with_empty_docstring(self, tmp_path, monkeypatch): + def test_extract_fallback_with_empty_docstring( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: """Test fallback when module has no docstring.""" # Create a file without a docstring test_file = tmp_path / "no_doc_fallback.py" test_file.write_text("x = 1\n") # Mock get_module_docstring to raise OSError to trigger fallback - def mock_get_module_docstring(path): + def mock_get_module_docstring(path: Path) -> NoReturn: raise OSError("Simulated error") import firecrown.fctools.list_tools as list_tools_module @@ -254,7 +262,7 @@ def mock_get_module_docstring(path): class TestDiscoverTools: # pylint: disable=import-outside-toplevel """Tests for _discover_tools function.""" - def test_discover_finds_tools(self): + def test_discover_finds_tools(self) -> None: """Test that tool discovery finds real fctools.""" tools = _discover_tools() @@ -265,19 +273,21 @@ def test_discover_finds_tools(self): assert all(isinstance(k, str) for k in tools) assert all(isinstance(v, str) for v in tools.values()) - def test_discover_excludes_init(self): + def test_discover_excludes_init(self) -> None: """Test that __init__.py is excluded from tools.""" tools = _discover_tools() assert "__init__.py" not in tools - def test_discover_excludes_list_tools(self): + def test_discover_excludes_list_tools(self) -> None: """Test that list_tools.py excludes itself.""" tools = _discover_tools() assert "list_tools.py" not in tools - def test_discover_excludes_private_files(self, tmp_path, monkeypatch): + def test_discover_excludes_private_files( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: """Test that files starting with _ are excluded.""" # First check the real discover_tools tools = _discover_tools() @@ -314,7 +324,7 @@ def test_discover_excludes_private_files(self, tmp_path, monkeypatch): # Restore original monkeypatch.setattr(list_tools_module, "__file__", original_file) - def test_discover_returns_descriptions(self): + def test_discover_returns_descriptions(self) -> None: """Test that discovered tools have descriptions.""" tools = _discover_tools() @@ -323,7 +333,7 @@ def test_discover_returns_descriptions(self): assert description assert len(description) > 0 - def test_discover_finds_specific_tools(self): + def test_discover_finds_specific_tools(self) -> None: """Test that known tools are discovered.""" tools = _discover_tools() @@ -337,7 +347,7 @@ def test_discover_finds_specific_tools(self): class TestMainFunction: # pylint: disable=import-outside-toplevel """Tests for main CLI function.""" - def test_main_basic_output(self): + def test_main_basic_output(self) -> None: """Test main function produces output.""" runner = CliRunner() result = runner.invoke(app, []) @@ -345,7 +355,7 @@ def test_main_basic_output(self): assert result.exit_code == 0 assert match_wrapped(result.stdout, "Available fctools:") - def test_main_lists_tools(self): + def test_main_lists_tools(self) -> None: """Test that main lists actual tools.""" runner = CliRunner() result = runner.invoke(app, []) @@ -355,7 +365,7 @@ def test_main_lists_tools(self): assert match_wrapped(result.stdout, "common.py") assert match_wrapped(result.stdout, "ast_utils.py") - def test_main_verbose_flag(self): + def test_main_verbose_flag(self) -> None: """Test main with --verbose flag.""" runner = CliRunner() result = runner.invoke(app, ["--verbose"]) @@ -364,7 +374,7 @@ def test_main_verbose_flag(self): assert match_wrapped(result.stdout, "Available fctools:") assert match_wrapped(result.stdout, "Usage: python -m firecrown.fctools.") - def test_main_short_verbose_flag(self): + def test_main_short_verbose_flag(self) -> None: """Test main with -v flag.""" runner = CliRunner() result = runner.invoke(app, ["-v"]) @@ -372,7 +382,7 @@ def test_main_short_verbose_flag(self): assert result.exit_code == 0 assert match_wrapped(result.stdout, "Usage: python -m firecrown.fctools.") - def test_main_non_verbose_has_help_text(self): + def test_main_non_verbose_has_help_text(self) -> None: """Test that non-verbose mode shows help text.""" runner = CliRunner() result = runner.invoke(app, []) @@ -383,7 +393,7 @@ def test_main_non_verbose_has_help_text(self): result.stdout, "Use 'python -m firecrown.fctools.TOOL --help'" ) - def test_main_verbose_shows_tool_details(self): + def test_main_verbose_shows_tool_details(self) -> None: """Test that verbose mode shows more details.""" runner = CliRunner() result = runner.invoke(app, ["--verbose"]) @@ -395,7 +405,7 @@ def test_main_verbose_shows_tool_details(self): has_usage_line = any("Usage: python -m" in line for line in lines) assert has_usage_line - def test_main_tools_are_sorted(self): + def test_main_tools_are_sorted(self) -> None: """Test that tools are listed in alphabetical order.""" runner = CliRunner() result = runner.invoke(app, []) @@ -419,7 +429,7 @@ def test_main_tools_are_sorted(self): if len(tool_names) > 1: assert tool_names == sorted(tool_names) - def test_main_with_subprocess(self): + def test_main_with_subprocess(self) -> None: """Test that the script can be executed directly via subprocess. This test verifies that the __main__ block and ImportError fallback @@ -438,7 +448,7 @@ def test_main_with_subprocess(self): assert match_wrapped(result.stdout, "Available fctools:") assert "common.py" in result.stdout - def test_main_subprocess_with_verbose(self): + def test_main_subprocess_with_verbose(self) -> None: """Test script execution with verbose flag via subprocess.""" script_path = "firecrown/fctools/list_tools.py" result = subprocess.run( @@ -456,7 +466,7 @@ def test_main_subprocess_with_verbose(self): class TestIntegration: # pylint: disable=import-outside-toplevel """Integration tests for list_tools functionality.""" - def test_full_workflow(self): + def test_full_workflow(self) -> None: """Test the complete workflow of discovering and listing tools.""" # Discover tools tools = _discover_tools() @@ -475,7 +485,7 @@ def test_full_workflow(self): tool_path.suffix == ".py" ), f"Tool {tool_name} should be a Python file" - def test_all_tools_have_valid_descriptions(self): + def test_all_tools_have_valid_descriptions(self) -> None: """Test that all discovered tools have meaningful descriptions.""" tools = _discover_tools() @@ -485,7 +495,7 @@ def test_all_tools_have_valid_descriptions(self): assert isinstance(description, str) assert len(description) > 0 - def test_cli_output_matches_discovery(self): + def test_cli_output_matches_discovery(self) -> None: """Test that CLI output matches tool discovery results.""" tools = _discover_tools() diff --git a/tests/fctools/test_match_wrapped.py b/tests/fctools/test_match_wrapped.py index dab0e4f66..9666acd1f 100644 --- a/tests/fctools/test_match_wrapped.py +++ b/tests/fctools/test_match_wrapped.py @@ -3,7 +3,7 @@ from . import match_wrapped -def test_match_wrapped(): +def test_match_wrapped() -> None: text = ( "Reading coverage data from \n" "/tmp/pytest-of-runner/pytest-0/popen-gw0/" diff --git a/tests/fctools/test_measurement_compatibility.py b/tests/fctools/test_measurement_compatibility.py index b6ed8d237..63d5fb5f3 100644 --- a/tests/fctools/test_measurement_compatibility.py +++ b/tests/fctools/test_measurement_compatibility.py @@ -6,6 +6,7 @@ import subprocess import sys +import pytest from rich.console import Console from firecrown.fctools.measurement_compatibility import ( @@ -31,7 +32,7 @@ class TestDiscoverMeasurementsBySpace: """Tests for discover_measurements_by_space function.""" - def test_discovers_measurements(self): + def test_discovers_measurements(self) -> None: """Test that measurements are discovered correctly.""" real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -43,7 +44,7 @@ def test_discovers_measurements(self): assert all(isinstance(m, Measurement) for m in real_measurements) assert all(isinstance(m, Measurement) for m in harmonic_measurements) - def test_real_measurements_support_real_space(self): + def test_real_measurements_support_real_space(self) -> None: """Test that all real measurements support real space.""" real_measurements, _ = discover_measurements_by_space() @@ -51,7 +52,7 @@ def test_real_measurements_support_real_space(self): for m in real_measurements: assert measurement_supports_real(m), f"{m} should support real space" - def test_harmonic_measurements_support_harmonic_space(self): + def test_harmonic_measurements_support_harmonic_space(self) -> None: """Test that all harmonic measurements support harmonic space.""" _, harmonic_measurements = discover_measurements_by_space() @@ -61,7 +62,7 @@ def test_harmonic_measurements_support_harmonic_space(self): m ), f"{m} should support harmonic space" - def test_measurements_are_subset_of_all(self): + def test_measurements_are_subset_of_all(self) -> None: """Test that discovered measurements are subsets of ALL_MEASUREMENTS.""" real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -69,7 +70,7 @@ def test_measurements_are_subset_of_all(self): assert set(real_measurements).issubset(set(ALL_MEASUREMENTS)) assert set(harmonic_measurements).issubset(set(ALL_MEASUREMENTS)) - def test_discovers_nonempty_lists(self): + def test_discovers_nonempty_lists(self) -> None: """Test that at least some measurements are discovered.""" real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -81,7 +82,7 @@ def test_discovers_nonempty_lists(self): class TestGenerateCompatiblePairs: """Tests for generate_compatible_pairs function.""" - def test_generates_pairs_for_real_space(self): + def test_generates_pairs_for_real_space(self) -> None: """Test generating compatible pairs for real space.""" real_measurements, _ = discover_measurements_by_space() @@ -94,7 +95,7 @@ def test_generates_pairs_for_real_space(self): assert all(isinstance(p, tuple) for p in pairs) assert all(len(p) == 2 for p in pairs) - def test_generates_pairs_for_harmonic_space(self): + def test_generates_pairs_for_harmonic_space(self) -> None: """Test generating compatible pairs for harmonic space.""" _, harmonic_measurements = discover_measurements_by_space() @@ -107,7 +108,7 @@ def test_generates_pairs_for_harmonic_space(self): assert all(isinstance(p, tuple) for p in pairs) assert all(len(p) == 2 for p in pairs) - def test_all_pairs_are_compatible(self): + def test_all_pairs_are_compatible(self) -> None: """Test that all generated pairs are actually compatible.""" real_measurements, _ = discover_measurements_by_space() @@ -121,7 +122,7 @@ def test_all_pairs_are_compatible(self): m1, m2 ), f"{m1} and {m2} should be compatible" - def test_pairs_are_from_input_measurements(self): + def test_pairs_are_from_input_measurements(self) -> None: """Test that pairs contain only measurements from input list.""" real_measurements, _ = discover_measurements_by_space() @@ -134,13 +135,13 @@ def test_pairs_are_from_input_measurements(self): assert m1 in real_measurements assert m2 in real_measurements - def test_empty_input_returns_empty_pairs(self): + def test_empty_input_returns_empty_pairs(self) -> None: """Test that empty measurement list returns empty pairs.""" pairs = generate_compatible_pairs([], measurement_is_compatible_real) assert pairs == [] - def test_single_measurement_returns_compatible_pairs(self): + def test_single_measurement_returns_compatible_pairs(self) -> None: """Test behavior with a single measurement.""" # Get one measurement real_measurements, _ = discover_measurements_by_space() @@ -160,7 +161,9 @@ def test_single_measurement_returns_compatible_pairs(self): class TestPrintMeasurementsBySpace: """Tests for print_measurements_by_space function.""" - def test_prints_counts_non_verbose(self, capsys): + def test_prints_counts_non_verbose( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test printing measurement counts in non-verbose mode.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -175,7 +178,7 @@ def test_prints_counts_non_verbose(self, capsys): assert match_wrapped(captured.out, str(len(real_measurements))) assert match_wrapped(captured.out, str(len(harmonic_measurements))) - def test_prints_details_verbose(self, capsys): + def test_prints_details_verbose(self, capsys: pytest.CaptureFixture[str]) -> None: """Test printing measurement details in verbose mode.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -197,7 +200,9 @@ def test_prints_details_verbose(self, capsys): class TestPrintCompatiblePairs: """Tests for print_compatible_pairs function.""" - def test_prints_pair_count_non_verbose(self, capsys): + def test_prints_pair_count_non_verbose( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test printing pair counts in non-verbose mode.""" console = Console() real_measurements, _ = discover_measurements_by_space() @@ -211,7 +216,9 @@ def test_prints_pair_count_non_verbose(self, capsys): assert match_wrapped(captured.out, "Valid real-space pairs:") assert match_wrapped(captured.out, str(len(pairs))) - def test_prints_pair_details_verbose(self, capsys): + def test_prints_pair_details_verbose( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test printing pair details in verbose mode.""" console = Console() real_measurements, _ = discover_measurements_by_space() @@ -230,7 +237,7 @@ def test_prints_pair_details_verbose(self, capsys): # Should show measurement names assert "+" in captured.out - def test_handles_empty_pairs(self, capsys): + def test_handles_empty_pairs(self, capsys: pytest.CaptureFixture[str]) -> None: """Test handling empty pairs list.""" console = Console() print_compatible_pairs(console, "test-space", [], False) @@ -242,7 +249,7 @@ def test_handles_empty_pairs(self, capsys): class TestPrintEfficiencyGains: """Tests for print_efficiency_gains function.""" - def test_prints_efficiency_stats(self, capsys): + def test_prints_efficiency_stats(self, capsys: pytest.CaptureFixture[str]) -> None: """Test printing efficiency statistics.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -268,7 +275,9 @@ def test_prints_efficiency_stats(self, capsys): assert match_wrapped(captured.out, "Total:") assert match_wrapped(captured.out, "skipped tests eliminated") - def test_calculates_correct_reduction(self, capsys): + def test_calculates_correct_reduction( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test that reduction calculations are correct.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -299,7 +308,9 @@ def test_calculates_correct_reduction(self, capsys): class TestPrintSummaryStats: """Tests for print_summary_stats function.""" - def test_prints_summary_statistics(self, capsys): + def test_prints_summary_statistics( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test printing summary statistics.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -326,7 +337,7 @@ def test_prints_summary_statistics(self, capsys): assert match_wrapped(captured.out, "Real-space compatibility:") assert match_wrapped(captured.out, "Harmonic-space compatibility:") - def test_shows_percentages(self, capsys): + def test_shows_percentages(self, capsys: pytest.CaptureFixture[str]) -> None: """Test that percentages are shown.""" console = Console() real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -353,7 +364,7 @@ def test_shows_percentages(self, capsys): class TestMainFunction: # pylint: disable=import-outside-toplevel """Tests for main CLI function.""" - def test_main_default_options(self, capsys): + def test_main_default_options(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with default options (space=BOTH, verbose=False).""" from firecrown.fctools.measurement_compatibility import Space, main @@ -367,7 +378,7 @@ def test_main_default_options(self, capsys): assert "Efficiency Improvements:" in captured.out assert "Summary Statistics:" in captured.out - def test_main_verbose_flag(self, capsys): + def test_main_verbose_flag(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with verbose=True.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -378,7 +389,9 @@ def test_main_verbose_flag(self, capsys): # In verbose mode, should show individual measurements assert "•" in captured.out - def test_main_verbose_with_space_harmonic(self, capsys): + def test_main_verbose_with_space_harmonic( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with verbose=True and space=HARMONIC.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -388,7 +401,7 @@ def test_main_verbose_with_space_harmonic(self, capsys): assert "•" in captured.out assert "harmonic-space" in captured.out - def test_main_space_real(self, capsys): + def test_main_space_real(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with space=REAL.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -401,7 +414,7 @@ def test_main_space_real(self, capsys): # Should not show harmonic-space pairs assert "harmonic-space" not in captured.out - def test_main_space_harmonic(self, capsys): + def test_main_space_harmonic(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with space=HARMONIC.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -412,7 +425,7 @@ def test_main_space_harmonic(self, capsys): # Should show efficiency for harmonic space assert "Harmonic-space efficiency:" in captured.out - def test_main_space_both(self, capsys): + def test_main_space_both(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with space=BOTH.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -423,7 +436,7 @@ def test_main_space_both(self, capsys): assert "harmonic-space" in captured.out assert "Summary Statistics:" in captured.out - def test_main_stats_only(self, capsys): + def test_main_stats_only(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with stats_only=True.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -436,7 +449,9 @@ def test_main_stats_only(self, capsys): # Should not show efficiency improvements assert "Efficiency Improvements:" not in captured.out - def test_main_stats_only_with_space_real(self, capsys): + def test_main_stats_only_with_space_real( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with stats_only=True and space=REAL.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -448,7 +463,9 @@ def test_main_stats_only_with_space_real(self, capsys): assert "Valid real-space pairs:" not in captured.out assert "Real-space efficiency:" not in captured.out - def test_main_stats_only_with_space_harmonic(self, capsys): + def test_main_stats_only_with_space_harmonic( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with stats_only=True and space=HARMONIC.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -460,7 +477,7 @@ def test_main_stats_only_with_space_harmonic(self, capsys): assert "Valid harmonic-space pairs:" not in captured.out assert "Harmonic-space efficiency:" not in captured.out - def test_main_combined_flags(self, capsys): + def test_main_combined_flags(self, capsys: pytest.CaptureFixture[str]) -> None: """Test main with verbose=True and space=REAL.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -472,7 +489,9 @@ def test_main_combined_flags(self, capsys): # Should show Real-space efficiency assert "Real-space efficiency:" in captured.out - def test_main_space_real_without_verbose(self, capsys): + def test_main_space_real_without_verbose( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with space=REAL and verbose=False.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -484,7 +503,9 @@ def test_main_space_real_without_verbose(self, capsys): # Should show Real-space efficiency assert "Real-space efficiency:" in captured.out - def test_main_space_harmonic_without_verbose(self, capsys): + def test_main_space_harmonic_without_verbose( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with space=HARMONIC and verbose=False.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -496,7 +517,9 @@ def test_main_space_harmonic_without_verbose(self, capsys): # Should not show real-space details when space=harmonic assert "Valid real-space pairs:" not in captured.out - def test_main_space_both_shows_all_sections(self, capsys): + def test_main_space_both_shows_all_sections( + self, capsys: pytest.CaptureFixture[str] + ) -> None: """Test main with space=BOTH shows all sections.""" from firecrown.fctools.measurement_compatibility import Space, main @@ -514,7 +537,7 @@ def test_main_space_both_shows_all_sections(self, capsys): # Should show summary statistics assert "Summary Statistics:" in captured.out - def test_main_with_subprocess(self): + def test_main_with_subprocess(self) -> None: """Test that the script can be executed directly via subprocess. This test verifies that the __main__ block works correctly. @@ -530,7 +553,7 @@ def test_main_with_subprocess(self): assert result.returncode == 0 assert "Firecrown Measurement Compatibility Analysis" in result.stdout - def test_main_subprocess_with_stats_only(self): + def test_main_subprocess_with_stats_only(self) -> None: """Test script execution with --stats-only flag.""" script_path = "firecrown/fctools/measurement_compatibility.py" result = subprocess.run( @@ -547,7 +570,7 @@ def test_main_subprocess_with_stats_only(self): class TestIntegration: """Integration tests for measurement compatibility functionality.""" - def test_full_workflow_real_space(self): + def test_full_workflow_real_space(self) -> None: """Test complete workflow for real space.""" # Discover measurements real_measurements, _ = discover_measurements_by_space() @@ -565,7 +588,7 @@ def test_full_workflow_real_space(self): for m1, m2 in pairs: assert measurement_is_compatible_real(m1, m2) - def test_full_workflow_harmonic_space(self): + def test_full_workflow_harmonic_space(self) -> None: """Test complete workflow for harmonic space.""" # Discover measurements _, harmonic_measurements = discover_measurements_by_space() @@ -583,7 +606,7 @@ def test_full_workflow_harmonic_space(self): for m1, m2 in pairs: assert measurement_is_compatible_harmonic(m1, m2) - def test_compatibility_is_subset_of_all_combinations(self): + def test_compatibility_is_subset_of_all_combinations(self) -> None: """Test that compatible pairs are a subset of all possible combinations.""" real_measurements, harmonic_measurements = discover_measurements_by_space() @@ -598,7 +621,7 @@ def test_compatibility_is_subset_of_all_combinations(self): assert len(real_pairs) <= len(real_measurements) ** 2 assert len(harmonic_pairs) <= len(harmonic_measurements) ** 2 - def test_cli_produces_consistent_output(self): + def test_cli_produces_consistent_output(self) -> None: """Test that CLI produces consistent output across runs.""" script_path = "firecrown/fctools/measurement_compatibility.py" @@ -621,7 +644,7 @@ def test_cli_produces_consistent_output(self): assert result2.returncode == 0 assert result1.stdout == result2.stdout - def test_all_measurements_categorized(self): + def test_all_measurements_categorized(self) -> None: """Test that measurements are properly categorized.""" real_measurements, harmonic_measurements = discover_measurements_by_space() diff --git a/tests/fctools/test_print_code.py b/tests/fctools/test_print_code.py index 5da48cf15..e4294e72e 100644 --- a/tests/fctools/test_print_code.py +++ b/tests/fctools/test_print_code.py @@ -27,7 +27,7 @@ # pylint: disable=missing-function-docstring,missing-class-docstring -def _get_subprocess_env(): +def _get_subprocess_env() -> dict[str, str]: """Get environment with PYTHONPATH set to include the current directory.""" env = os.environ.copy() # Add current directory to PYTHONPATH so subprocess can import tests module @@ -70,7 +70,7 @@ class ClassNoDocstring: # Tests for _render_attributes -def test_render_attributes_simple(): +def test_render_attributes_simple() -> None: """Test _render_attributes with simple attributes.""" # Create a simple ClassDef with attributes class_def = _make_class_def( @@ -98,7 +98,7 @@ def test_render_attributes_simple(): assert any("attr2: str = 'default'" in line for line in result) -def test_render_attributes_empty(): +def test_render_attributes_empty() -> None: """Test _render_attributes with empty class body.""" class_def = _make_class_def( name="EmptyClass", @@ -111,7 +111,7 @@ def test_render_attributes_empty(): assert not result -def test_render_attributes_no_annotation(): +def test_render_attributes_no_annotation() -> None: """Test _render_attributes with attribute without annotation.""" assign_node = ast.Assign( targets=[ast.Name(id="attr")], value=ast.Constant(value=42) @@ -129,7 +129,7 @@ def test_render_attributes_no_annotation(): assert any("attr = 42" in line for line in result) -def test_render_attributes_complex_value(): +def test_render_attributes_complex_value() -> None: """Test _render_attributes with complex attribute value.""" class_def = _make_class_def( name="TestClass", @@ -152,7 +152,7 @@ def test_render_attributes_complex_value(): # Tests for _build_class_code -def test_build_class_code_simple_class(): +def test_build_class_code_simple_class() -> None: """Test _build_class_code with a simple class.""" code = _build_class_code(SimpleClass) assert "class SimpleClass():" in code @@ -161,7 +161,7 @@ def test_build_class_code_simple_class(): assert "attr2: str = 'default'" in code -def test_build_class_code_decorated_class(): +def test_build_class_code_decorated_class() -> None: """Test _build_class_code with a decorated class.""" code = _build_class_code(DecoratedClass) assert "@dataclass" in code @@ -169,7 +169,7 @@ def test_build_class_code_decorated_class(): assert "value: int" in code -def test_build_class_code_class_with_bases(): +def test_build_class_code_class_with_bases() -> None: """Test _build_class_code with a class that has base classes.""" code = _build_class_code(ClassWithBases) # This should include the base class - exact format depends on AST @@ -177,21 +177,21 @@ def test_build_class_code_class_with_bases(): assert "attr3: float = 1.0" in code -def test_build_class_code_no_docstring(): +def test_build_class_code_no_docstring() -> None: """Test _build_class_code with a class without docstring.""" code = _build_class_code(ClassNoDocstring) assert "class ClassNoDocstring():" in code assert "attr: int" in code -def test_build_class_code_builtin_class(): +def test_build_class_code_builtin_class() -> None: """Test _build_class_code with a built-in class.""" # Built-in classes should raise TypeError (no source file) with pytest.raises(TypeError): _build_class_code(int) -def test_build_class_code_no_attributes(): +def test_build_class_code_no_attributes() -> None: """Test _build_class_code with a class that has no attributes.""" # Use a class defined at module level to avoid indentation issues code = _build_class_code(ClassNoDocstring) @@ -202,7 +202,7 @@ def test_build_class_code_no_attributes(): # Tests for display_class_attributes -def test_display_class_attributes_simple(capsys): +def test_display_class_attributes_simple(capsys: pytest.CaptureFixture[str]) -> None: """Test display_class_attributes with a simple class.""" display_class_attributes(SimpleClass) captured = capsys.readouterr() @@ -212,14 +212,14 @@ def test_display_class_attributes_simple(capsys): assert "```" in captured.out -def test_display_class_attributes_builtin_class(): +def test_display_class_attributes_builtin_class() -> None: """Test display_class_attributes with a built-in class.""" # Built-in classes raise TypeError, not OSError, so it will propagate with pytest.raises(TypeError, match="built-in class"): display_class_attributes(int) -def test_display_class_attributes_decorated(capsys): +def test_display_class_attributes_decorated(capsys: pytest.CaptureFixture[str]) -> None: """Test display_class_attributes with a decorated class.""" display_class_attributes(DecoratedClass) captured = capsys.readouterr() @@ -227,7 +227,9 @@ def test_display_class_attributes_decorated(capsys): assert "class DecoratedClass():" in captured.out -def test_display_class_attributes_no_markdown_simple(capsys): +def test_display_class_attributes_no_markdown_simple( + capsys: pytest.CaptureFixture[str], +) -> None: """Test display_class_attributes with markdown disabled.""" # This should just call display_class_without_markdown display_class_attributes(SimpleClass) @@ -238,7 +240,9 @@ def test_display_class_attributes_no_markdown_simple(capsys): # Tests for display_class_without_markdown -def test_display_class_without_markdown_simple(capsys): +def test_display_class_without_markdown_simple( + capsys: pytest.CaptureFixture[str], +) -> None: """Test display_class_without_markdown with a simple class.""" display_class_without_markdown(SimpleClass) captured = capsys.readouterr() @@ -249,14 +253,16 @@ def test_display_class_without_markdown_simple(capsys): assert "```" not in captured.out -def test_display_class_without_markdown_builtin_class(): +def test_display_class_without_markdown_builtin_class() -> None: """Test display_class_without_markdown with a built-in class.""" # Built-in classes raise TypeError, not OSError, so it will propagate with pytest.raises(TypeError, match="built-in class"): display_class_without_markdown(int) -def test_display_class_without_markdown_decorated(capsys): +def test_display_class_without_markdown_decorated( + capsys: pytest.CaptureFixture[str], +) -> None: """Test display_class_without_markdown with a decorated class.""" display_class_without_markdown(DecoratedClass) captured = capsys.readouterr() @@ -267,7 +273,7 @@ def test_display_class_without_markdown_decorated(capsys): # Tests for main CLI -def test_main_simple_class(): +def test_main_simple_class() -> None: """Test main CLI with a simple class path.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -282,7 +288,7 @@ def test_main_simple_class(): assert match_wrapped(result.stdout, "attr1: int") -def test_main_no_markdown(): +def test_main_no_markdown() -> None: """Test main CLI with --no-markdown flag.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -302,7 +308,7 @@ def test_main_no_markdown(): assert "```python" not in result.stdout -def test_main_decorated_class(): +def test_main_decorated_class() -> None: """Test main CLI with a decorated class.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -317,7 +323,7 @@ def test_main_decorated_class(): assert match_wrapped(result.stdout, "class DecoratedClass():") -def test_main_class_with_bases(): +def test_main_class_with_bases() -> None: """Test main CLI with a class that has base classes.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -332,7 +338,7 @@ def test_main_class_with_bases(): assert "attr3: float = 1.0" in result.stdout -def test_main_invalid_class_path(): +def test_main_invalid_class_path() -> None: """Test main CLI with invalid class path.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -345,7 +351,7 @@ def test_main_invalid_class_path(): assert result.returncode != 0 -def test_main_builtin_class(): +def test_main_builtin_class() -> None: """Test main CLI with built-in class.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -358,7 +364,7 @@ def test_main_builtin_class(): assert result.returncode != 0 -def test_main_missing_argument(): +def test_main_missing_argument() -> None: """Test main CLI with missing argument.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -371,7 +377,7 @@ def test_main_missing_argument(): assert "Missing argument" in result.stderr -def test_main_help(): +def test_main_help() -> None: """Test main CLI with --help flag.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -386,7 +392,7 @@ def test_main_help(): assert match_wrapped(strip_rich_markup(result.stdout), "--no-markdown") -def test_main_multiple_classes(): +def test_main_multiple_classes() -> None: """Test main CLI with multiple class names.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -415,7 +421,7 @@ def test_main_multiple_classes(): assert "class DecoratedClass()" in result.stdout -def test_main_multiple_classes_with_invalid(): +def test_main_multiple_classes_with_invalid() -> None: """Test main CLI with multiple classes where one is invalid.""" script_path = "firecrown/fctools/print_code.py" # Test with invalid module @@ -440,7 +446,7 @@ def test_main_multiple_classes_with_invalid(): ) -def test_build_class_code_with_none_class_def(): +def test_build_class_code_with_none_class_def() -> None: """Test _build_class_code when get_class_definition returns None.""" # This tests the error path when class definition can't be found # The ValueError is raised when class_def is None @@ -461,7 +467,7 @@ class TestClass: # Integration tests -def test_integration_full_workflow(): +def test_integration_full_workflow() -> None: """Test full workflow from CLI to output.""" script_path = "firecrown/fctools/print_code.py" result = subprocess.run( @@ -479,7 +485,7 @@ def test_integration_full_workflow(): assert "attr2: str = 'default'" in result.stdout -def test_integration_multiple_runs(): +def test_integration_multiple_runs() -> None: """Test that multiple runs work correctly.""" script_path = "firecrown/fctools/print_code.py" # Run twice with same class @@ -502,7 +508,7 @@ def test_integration_multiple_runs(): assert result1.stdout == result2.stdout -def test_integration_error_handling(): +def test_integration_error_handling() -> None: """Test error handling in full workflow.""" script_path = "firecrown/fctools/print_code.py" # Test with invalid path @@ -518,7 +524,7 @@ def test_integration_error_handling(): # Direct main() function tests for coverage -def test_main_function_single_class(capsys): +def test_main_function_single_class(capsys: pytest.CaptureFixture[str]) -> None: """Test main function directly with single class.""" # Call main with a single class main(class_names=["tests.fctools.test_print_code.SimpleClass"], no_markdown=False) @@ -529,7 +535,9 @@ def test_main_function_single_class(capsys): assert "A simple test class." in captured.out -def test_main_function_no_markdown_flag(capsys): +def test_main_function_no_markdown_flag( + capsys: pytest.CaptureFixture[str], +) -> None: """Test main function with no_markdown=True.""" # Call main with no_markdown flag main(class_names=["tests.fctools.test_print_code.SimpleClass"], no_markdown=True) @@ -540,7 +548,7 @@ def test_main_function_no_markdown_flag(capsys): assert "```python" not in captured.out -def test_main_function_multiple_classes(capsys): +def test_main_function_multiple_classes(capsys: pytest.CaptureFixture[str]) -> None: """Test main function with multiple classes.""" # Call main with multiple classes main( @@ -561,7 +569,9 @@ def test_main_function_multiple_classes(capsys): assert "Class: tests.fctools.test_print_code.DecoratedClass" in captured.out -def test_main_function_multiple_classes_no_markdown(capsys): +def test_main_function_multiple_classes_no_markdown( + capsys: pytest.CaptureFixture[str], +) -> None: """Test main function with multiple classes and no_markdown=True.""" # Call main with multiple classes and no_markdown main( diff --git a/tests/fctools/test_print_hierarchy.py b/tests/fctools/test_print_hierarchy.py index 8195b1aae..16fa80c6d 100644 --- a/tests/fctools/test_print_hierarchy.py +++ b/tests/fctools/test_print_hierarchy.py @@ -9,6 +9,8 @@ import subprocess import sys +import pytest + from firecrown.fctools.print_hierarchy import ( full_type_name, get_defined_methods, @@ -22,13 +24,13 @@ # Tests for full_type_name() -def test_builtin_type(): +def test_builtin_type() -> None: """Test getting full name for built-in type.""" result = full_type_name(int) assert result == "builtins.int" -def test_standard_library_type(): +def test_standard_library_type() -> None: """Test getting full name for standard library type.""" from collections import OrderedDict # pylint: disable=import-outside-toplevel @@ -36,7 +38,7 @@ def test_standard_library_type(): assert result == "collections.OrderedDict" -def test_custom_class(): +def test_custom_class() -> None: """Test getting full name for a custom class.""" class MyClass: @@ -50,7 +52,7 @@ class MyClass: # Tests for get_defined_methods() -def test_class_with_no_methods(): +def test_class_with_no_methods() -> None: """Test getting methods from a class with no methods.""" class EmptyClass: @@ -61,28 +63,28 @@ class EmptyClass: assert len(methods) == 0 -def test_class_with_simple_method(): +def test_class_with_simple_method() -> None: """Test getting methods from a class with one method.""" class SimpleClass: - def my_method(self): + def my_method(self) -> None: pass methods = get_defined_methods(SimpleClass) assert "my_method" in methods -def test_class_with_multiple_methods(): +def test_class_with_multiple_methods() -> None: """Test getting methods from a class with multiple methods.""" class MultiMethodClass: - def method_one(self): + def method_one(self) -> None: pass - def method_two(self): + def method_two(self) -> None: pass - def method_three(self): + def method_three(self) -> None: pass methods = get_defined_methods(MultiMethodClass) @@ -92,15 +94,15 @@ def method_three(self): assert "method_three" in methods -def test_inherited_method_not_included(): +def test_inherited_method_not_included() -> None: """Test that inherited methods are not included.""" class BaseClass: - def base_method(self): + def base_method(self) -> None: pass class DerivedClass(BaseClass): - def derived_method(self): + def derived_method(self) -> None: pass methods = get_defined_methods(DerivedClass) @@ -108,29 +110,29 @@ def derived_method(self): assert "base_method" not in methods -def test_overridden_method_included(): +def test_overridden_method_included() -> None: """Test that overridden methods are included.""" class BaseClass: - def my_method(self): + def my_method(self) -> str: return "base" class DerivedClass(BaseClass): - def my_method(self): + def my_method(self) -> str: return "derived" methods = get_defined_methods(DerivedClass) assert "my_method" in methods -def test_special_methods_included(): +def test_special_methods_included() -> None: """Test that special methods are included.""" class WithInit: - def __init__(self): + def __init__(self) -> None: pass - def __str__(self): + def __str__(self) -> str: return "test" methods = get_defined_methods(WithInit) @@ -138,19 +140,19 @@ def __str__(self): assert "__str__" in methods -def test_class_with_static_and_class_methods(): +def test_class_with_static_and_class_methods() -> None: """Test that static and class methods are not included.""" class WithSpecialMethods: - def regular_method(self): + def regular_method(self) -> None: pass @staticmethod - def static_method(): + def static_method() -> None: pass @classmethod - def class_method(cls): + def class_method(cls) -> None: pass methods = get_defined_methods(WithSpecialMethods) @@ -163,11 +165,11 @@ def class_method(cls): # Tests for print_one_type() -def test_prints_type_info(capsys): +def test_prints_type_info(capsys: pytest.CaptureFixture[str]) -> None: """Test printing basic type information.""" class SimpleClass: - def my_method(self): + def my_method(self) -> None: pass print_one_type(0, SimpleClass) @@ -179,14 +181,14 @@ def my_method(self): assert "my_method" in captured.out -def test_prints_multiple_methods(capsys): +def test_prints_multiple_methods(capsys: pytest.CaptureFixture[str]) -> None: """Test printing type with multiple methods.""" class MultiMethodClass: - def method_a(self): + def method_a(self) -> None: pass - def method_b(self): + def method_b(self) -> None: pass print_one_type(1, MultiMethodClass) @@ -198,7 +200,7 @@ def method_b(self): assert "method_b" in captured.out -def test_prints_with_different_indices(capsys): +def test_prints_with_different_indices(capsys: pytest.CaptureFixture[str]) -> None: """Test printing with different index values.""" class TestClass: @@ -214,7 +216,7 @@ class TestClass: # Tests for print_type_hierarchy() -def test_prints_simple_class(capsys): +def test_prints_simple_class(capsys: pytest.CaptureFixture[str]) -> None: """Test printing hierarchy for a simple class.""" class SimpleClass: @@ -229,19 +231,19 @@ class SimpleClass: assert "builtins.object" in captured.out -def test_prints_inheritance_chain(capsys): +def test_prints_inheritance_chain(capsys: pytest.CaptureFixture[str]) -> None: """Test printing hierarchy with inheritance.""" class Base: - def base_method(self): + def base_method(self) -> None: pass class Middle(Base): - def middle_method(self): + def middle_method(self) -> None: pass class Derived(Middle): - def derived_method(self): + def derived_method(self) -> None: pass print_type_hierarchy(Derived) @@ -259,7 +261,9 @@ def derived_method(self): assert "base_method" in captured.out -def test_prints_builtin_type_hierarchy(capsys): +def test_prints_builtin_type_hierarchy( + capsys: pytest.CaptureFixture[str], +) -> None: """Test printing hierarchy for built-in types.""" from collections import OrderedDict # pylint: disable=import-outside-toplevel @@ -275,7 +279,7 @@ def test_prints_builtin_type_hierarchy(capsys): # Tests for main() -def test_main_direct_single_typename(capsys): +def test_main_direct_single_typename(capsys: pytest.CaptureFixture[str]) -> None: """Test main function directly with single typename for coverage. This test calls the main() function directly (not via subprocess) @@ -291,7 +295,7 @@ def test_main_direct_single_typename(capsys): assert "=" not in captured.out or captured.out.count("=") < 10 -def test_main_direct_multiple_typenames(capsys): +def test_main_direct_multiple_typenames(capsys: pytest.CaptureFixture[str]) -> None: """Test main function directly with multiple typenames for coverage. This test calls the main() function directly (not via subprocess) @@ -311,7 +315,7 @@ def test_main_direct_multiple_typenames(capsys): assert "=" * 60 in captured.out -def test_main_with_single_typename(): +def test_main_with_single_typename() -> None: """Test main with a single type name.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -326,7 +330,7 @@ def test_main_with_single_typename(): assert match_wrapped(result.stdout, "OrderedDict") -def test_main_with_multiple_typenames(): +def test_main_with_multiple_typenames() -> None: """Test main with multiple type names.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -343,7 +347,7 @@ def test_main_with_multiple_typenames(): assert "=" in result.stdout -def test_main_with_no_arguments(): +def test_main_with_no_arguments() -> None: """Test main with no arguments (should fail).""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -357,7 +361,7 @@ def test_main_with_no_arguments(): # Should show error about missing argument -def test_main_with_invalid_typename(): +def test_main_with_invalid_typename() -> None: """Test main with invalid type name.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -373,7 +377,7 @@ def test_main_with_invalid_typename(): assert "nonexistent" in result.stderr -def test_main_with_mixed_valid_invalid(): +def test_main_with_mixed_valid_invalid() -> None: """Test main with mix of valid and invalid type names.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -397,7 +401,7 @@ def test_main_with_mixed_valid_invalid(): assert "Could not import module" in result.stderr -def test_main_with_builtin_type(): +def test_main_with_builtin_type() -> None: """Test main with built-in type.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -411,7 +415,7 @@ def test_main_with_builtin_type(): assert "int" in result.stdout -def test_main_with_subprocess(): +def test_main_with_subprocess() -> None: """Test that the script can be executed directly via subprocess. This test verifies that the __main__ block works correctly. @@ -429,7 +433,7 @@ def test_main_with_subprocess(): assert "Path" in result.stdout -def test_main_subprocess_with_multiple_types(): +def test_main_subprocess_with_multiple_types() -> None: """Test script execution with multiple type arguments.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -445,7 +449,7 @@ def test_main_subprocess_with_multiple_types(): assert match_wrapped(result.stdout, "=") -def test_main_subprocess_with_invalid_type(): +def test_main_subprocess_with_invalid_type() -> None: """Test script execution with invalid type name.""" script_path = "firecrown/fctools/print_hierarchy.py" result = subprocess.run( @@ -465,7 +469,9 @@ def test_main_subprocess_with_invalid_type(): # Integration tests -def test_full_workflow_with_standard_type(capsys): +def test_full_workflow_with_standard_type( + capsys: pytest.CaptureFixture[str], +) -> None: """Test complete workflow with a standard library type.""" from collections import OrderedDict # pylint: disable=import-outside-toplevel @@ -484,19 +490,21 @@ def test_full_workflow_with_standard_type(capsys): assert "dict" in captured.out -def test_full_workflow_with_custom_hierarchy(capsys): +def test_full_workflow_with_custom_hierarchy( + capsys: pytest.CaptureFixture[str], +) -> None: """Test complete workflow with custom class hierarchy.""" class GrandParent: - def grandparent_method(self): + def grandparent_method(self) -> None: pass class Parent(GrandParent): - def parent_method(self): + def parent_method(self) -> None: pass class Child(Parent): - def child_method(self): + def child_method(self) -> None: pass # Test each function @@ -515,7 +523,7 @@ def child_method(self): assert "grandparent_method" in captured.out -def test_cli_produces_consistent_output(): +def test_cli_produces_consistent_output() -> None: """Test that CLI produces consistent output across runs.""" script_path = "firecrown/fctools/print_hierarchy.py" @@ -539,7 +547,7 @@ def test_cli_produces_consistent_output(): assert result1.stdout == result2.stdout -def test_handles_complex_inheritance(): +def test_handles_complex_inheritance() -> None: """Test handling complex inheritance patterns.""" script_path = "firecrown/fctools/print_hierarchy.py" # OrderedDict has complex MRO @@ -558,21 +566,21 @@ def test_handles_complex_inheritance(): assert len(hierarchy_lines) > 2 # At least OrderedDict, dict, object -def test_method_detection_accuracy(): +def test_method_detection_accuracy() -> None: """Test that method detection is accurate.""" class Base: - def base_only(self): + def base_only(self) -> None: pass - def to_override(self): + def to_override(self) -> None: pass class Derived(Base): - def derived_only(self): + def derived_only(self) -> None: pass - def to_override(self): + def to_override(self) -> None: pass # Override base_methods = get_defined_methods(Base) diff --git a/tests/fctools/test_symbol_reference_checker.py b/tests/fctools/test_symbol_reference_checker.py index 08578851a..28a5a71e3 100644 --- a/tests/fctools/test_symbol_reference_checker.py +++ b/tests/fctools/test_symbol_reference_checker.py @@ -1,6 +1,7 @@ """Tests for the symbol_reference_checker module.""" import json +from pathlib import Path import pytest from typer.testing import CliRunner @@ -16,7 +17,7 @@ runner = CliRunner() -def test_extract_code_spans_simple(): +def test_extract_code_spans_simple() -> None: """Test extracting code spans from simple markdown.""" content = "This is `code1` and `code2` in text." spans = extract_code_spans(content) @@ -25,7 +26,7 @@ def test_extract_code_spans_simple(): assert spans[1] == (1, "code2") -def test_extract_code_spans_multiline(): +def test_extract_code_spans_multiline() -> None: """Test extracting code spans from multiple lines.""" content = """Line 1 with `span1` Line 2 with `span2` and `span3` @@ -39,14 +40,14 @@ def test_extract_code_spans_multiline(): assert spans[3] == (4, "span4") -def test_extract_code_spans_empty(): +def test_extract_code_spans_empty() -> None: """Test extracting code spans from text without any.""" content = "No code spans here at all." spans = extract_code_spans(content) assert len(spans) == 0 -def test_check_qmd_file_valid_symbols(tmp_path): +def test_check_qmd_file_valid_symbols(tmp_path: Path) -> None: """Test checking a file with valid symbol references.""" # Create a test .qmd file qmd_file = tmp_path / "test.qmd" @@ -66,7 +67,7 @@ def test_check_qmd_file_valid_symbols(tmp_path): assert len(errors) == 0 -def test_check_qmd_file_invalid_fully_qualified(tmp_path): +def test_check_qmd_file_invalid_fully_qualified(tmp_path: Path) -> None: """Test checking a file with invalid fully-qualified symbol.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -83,7 +84,7 @@ def test_check_qmd_file_invalid_fully_qualified(tmp_path): assert "Line 2" in errors[0] -def test_check_qmd_file_invalid_unqualified(tmp_path): +def test_check_qmd_file_invalid_unqualified(tmp_path: Path) -> None: """Test checking a file with invalid unqualified symbol.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -99,7 +100,7 @@ def test_check_qmd_file_invalid_unqualified(tmp_path): assert "Updateable" in errors[0] -def test_check_qmd_file_multiple_errors(tmp_path): +def test_check_qmd_file_multiple_errors(tmp_path: Path) -> None: """Test that all errors are reported, not just the first.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -119,7 +120,7 @@ def test_check_qmd_file_multiple_errors(tmp_path): assert any("InvalidClass3" in e for e in errors) -def test_check_qmd_file_with_exclude_pattern(tmp_path): +def test_check_qmd_file_with_exclude_pattern(tmp_path: Path) -> None: """Test that exclude pattern works.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -141,7 +142,7 @@ def test_check_qmd_file_with_exclude_pattern(tmp_path): assert len(errors_with_exclude) == 0 -def test_check_qmd_file_lowercase_code_not_checked(tmp_path): +def test_check_qmd_file_lowercase_code_not_checked(tmp_path: Path) -> None: """Test that lowercase code spans are not checked as symbols.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -155,7 +156,7 @@ def test_check_qmd_file_lowercase_code_not_checked(tmp_path): assert len(errors) == 0 -def test_check_qmd_file_partial_module_path(tmp_path): +def test_check_qmd_file_partial_module_path(tmp_path: Path) -> None: """Test validation of partial module paths.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text(""" @@ -171,7 +172,7 @@ def test_check_qmd_file_partial_module_path(tmp_path): assert len(errors) == 0 -def test_main_success(tmp_path): +def test_main_success(tmp_path: Path) -> None: """Test main function with valid symbols.""" # Create test files qmd_file = tmp_path / "test.qmd" @@ -189,7 +190,7 @@ def test_main_success(tmp_path): assert "No invalid symbol references" in result.stdout -def test_main_with_errors(tmp_path): +def test_main_with_errors(tmp_path: Path) -> None: """Test main function with invalid symbols.""" # Create test files qmd_file = tmp_path / "test.qmd" @@ -207,21 +208,22 @@ def test_main_with_errors(tmp_path): assert "firecrown.invalid.Symbol" in result.stderr -def test_main_missing_symbol_map(tmp_path): +def test_main_missing_symbol_map(tmp_path: Path) -> None: """Test main function with missing symbol map file.""" result = runner.invoke(app, [str(tmp_path), str(tmp_path / "nonexistent.json")]) assert result.exit_code != 0 -def test_validate_symbol_map_rejects_non_string_value(): +def test_validate_symbol_map_rejects_non_string_value() -> None: + symbol_map: dict[str, object] = {"symbol": 123} with pytest.raises(SystemExit) as exc_info: - _validate_symbol_map({"symbol": 123}) + _validate_symbol_map(symbol_map) assert exc_info.value.code == 1 -def test_main_with_exclude_pattern(tmp_path): +def test_main_with_exclude_pattern(tmp_path: Path) -> None: """Test main function with exclude pattern.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text("Example: `firecrown.example.ExampleClass`") @@ -241,7 +243,9 @@ def test_main_with_exclude_pattern(tmp_path): assert result.exit_code == 0 -def test_load_external_symbols_read_error(tmp_path, capsys): +def test_load_external_symbols_read_error( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test _load_external_symbols handles OSError gracefully.""" # pylint: disable=import-outside-toplevel from firecrown.fctools.symbol_reference_checker import ( @@ -263,7 +267,9 @@ def test_load_external_symbols_read_error(tmp_path, capsys): assert "Warning" in captured.err or captured.err == "" -def test_load_external_symbols_unicode_error(tmp_path, capsys): +def test_load_external_symbols_unicode_error( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test _load_external_symbols handles UnicodeDecodeError gracefully.""" # pylint: disable=import-outside-toplevel from firecrown.fctools.symbol_reference_checker import ( @@ -284,7 +290,7 @@ def test_load_external_symbols_unicode_error(tmp_path, capsys): assert "Warning" in captured.err or captured.err == "" -def test_load_external_symbols_with_comments_and_empty_lines(tmp_path): +def test_load_external_symbols_with_comments_and_empty_lines(tmp_path: Path) -> None: """Test _load_external_symbols filters comments and empty lines.""" # pylint: disable=import-outside-toplevel from firecrown.fctools.symbol_reference_checker import ( @@ -309,7 +315,9 @@ def test_load_external_symbols_with_comments_and_empty_lines(tmp_path): assert result == {"ValidSymbol1", "ValidSymbol2", "ValidSymbol3"} -def test_compile_exclude_pattern_invalid_regex(capsys): +def test_compile_exclude_pattern_invalid_regex( + capsys: pytest.CaptureFixture[str], +) -> None: """Test _compile_exclude_pattern handles invalid regex.""" # pylint: disable=import-outside-toplevel from firecrown.fctools.symbol_reference_checker import ( @@ -327,7 +335,9 @@ def test_compile_exclude_pattern_invalid_regex(capsys): assert "Warning" in captured.err -def test_check_qmd_file_unqualified_symbol_also_fully_qualified(tmp_path): +def test_check_qmd_file_unqualified_symbol_also_fully_qualified( + tmp_path: Path, +) -> None: """Test unqualified symbol that exists in symbol map without dots. This tests the case where a symbol like `CustomClass` is in the symbol @@ -350,7 +360,7 @@ def test_check_qmd_file_unqualified_symbol_also_fully_qualified(tmp_path): assert len(errors) == 0 -def test_main_with_external_symbols_file(tmp_path): +def test_main_with_external_symbols_file(tmp_path: Path) -> None: """Test main function with external symbols file.""" # Create test files qmd_file = tmp_path / "test.qmd" @@ -378,7 +388,9 @@ def test_main_with_external_symbols_file(tmp_path): assert "No invalid symbol references" in result.stdout -def test_check_qmd_file_with_invalid_exclude_pattern(tmp_path, capsys): +def test_check_qmd_file_with_invalid_exclude_pattern( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test check_qmd_file handles invalid exclude pattern gracefully.""" qmd_file = tmp_path / "test.qmd" qmd_file.write_text("Reference: `firecrown.invalid.Symbol`") @@ -396,10 +408,8 @@ def test_check_qmd_file_with_invalid_exclude_pattern(tmp_path, capsys): assert "Warning" in captured.err -def test_check_qmd_file_unqualified_not_in_fully_qualified(): +def test_check_qmd_file_unqualified_not_in_fully_qualified() -> None: """Test check_qmd_file with unqualified symbol not in fully_qualified set.""" - from pathlib import Path # pylint: disable=import-outside-toplevel - qmd_file = Path("test.qmd") # Use an uppercase unqualified symbol that doesn't exist content = "Reference to `UnknownSymbol` in text." diff --git a/tests/fctools/test_tracer.py b/tests/fctools/test_tracer.py index e4c495377..945089d75 100644 --- a/tests/fctools/test_tracer.py +++ b/tests/fctools/test_tracer.py @@ -12,7 +12,7 @@ import types from pathlib import Path from types import FrameType -from typing import cast +from typing import Generator, cast import pytest @@ -47,7 +47,7 @@ def _make_frame( @pytest.fixture(autouse=True) -def cleanup_tracing(): +def cleanup_tracing() -> Generator[None, None, None]: """Ensure sys.settrace is always cleaned up after each test. This fixture runs automatically for all tests and ensures that tracing @@ -66,7 +66,7 @@ def cleanup_tracing(): # Tests for TracerState.__init__() -def test_tracer_state_init_creates_file(tmp_path): +def test_tracer_state_init_creates_file(tmp_path: Path) -> None: """Test that TracerState creates a trace file.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -79,7 +79,7 @@ def test_tracer_state_init_creates_file(tmp_path): tracer.close() -def test_tracer_state_init_writes_header(tmp_path): +def test_tracer_state_init_writes_header(tmp_path: Path) -> None: """Test that TracerState writes header line.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -92,7 +92,9 @@ def test_tracer_state_init_writes_header(tmp_path): tracer.close() -def test_tracer_state_default_filename(tmp_path, monkeypatch): +def test_tracer_state_default_filename( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that TracerState uses default filename.""" monkeypatch.chdir(tmp_path) tracer = TracerState() @@ -107,7 +109,7 @@ def test_tracer_state_default_filename(tmp_path, monkeypatch): # Tests for TracerState.trace_call() - call events -def test_trace_call_event(tmp_path): +def test_trace_call_event(tmp_path: Path) -> None: """Test tracing a function call event.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -130,13 +132,13 @@ def test_trace_call_event(tmp_path): tracer.close() -def test_trace_call_with_self_argument(tmp_path): +def test_trace_call_with_self_argument(tmp_path: Path) -> None: """Test tracing a method call with self argument.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) class TestClass: - def method(self): + def method(self) -> None: pass obj = TestClass() @@ -156,7 +158,7 @@ def method(self): tracer.close() -def test_trace_call_no_arguments(tmp_path): +def test_trace_call_no_arguments(tmp_path: Path) -> None: """Test tracing a function with no arguments.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -177,7 +179,7 @@ def test_trace_call_no_arguments(tmp_path): # Tests for TracerState.trace_call() - return events -def test_trace_return_event(tmp_path): +def test_trace_return_event(tmp_path: Path) -> None: """Test tracing a function return event.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -198,7 +200,7 @@ def test_trace_return_event(tmp_path): tracer.close() -def test_trace_return_with_none(tmp_path): +def test_trace_return_with_none(tmp_path: Path) -> None: """Test tracing a return with None value.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -218,14 +220,14 @@ def test_trace_return_with_none(tmp_path): tracer.close() -def test_trace_return_with_unprintable_object(tmp_path): +def test_trace_return_with_unprintable_object(tmp_path: Path) -> None: """Test tracing a return with object that can't be converted to str.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) tracer.level = 1 class UnprintableObject: - def __str__(self): + def __str__(self) -> str: raise AttributeError("Cannot convert to string") obj = UnprintableObject() @@ -243,14 +245,14 @@ def __str__(self): tracer.close() -def test_trace_return_with_recursion_error(tmp_path): +def test_trace_return_with_recursion_error(tmp_path: Path) -> None: """Test tracing a return with object that causes RecursionError on str().""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) tracer.level = 1 class RecursiveObject: - def __str__(self): + def __str__(self) -> str: raise RecursionError("Maximum recursion depth exceeded") obj = RecursiveObject() @@ -268,14 +270,14 @@ def __str__(self): tracer.close() -def test_trace_return_with_type_error(tmp_path): +def test_trace_return_with_type_error(tmp_path: Path) -> None: """Test tracing a return with object that causes TypeError on str().""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) tracer.level = 1 class TypeErrorObject: - def __str__(self): + def __str__(self) -> str: raise TypeError("Cannot convert to string") obj = TypeErrorObject() @@ -296,7 +298,7 @@ def __str__(self): # Tests for TracerState.trace_call() - exception events -def test_trace_exception_event(tmp_path): +def test_trace_exception_event(tmp_path: Path) -> None: """Test tracing an exception event.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -316,7 +318,7 @@ def test_trace_exception_event(tmp_path): tracer.close() -def test_trace_call_returns_itself(tmp_path): +def test_trace_call_returns_itself(tmp_path: Path) -> None: """Test that trace_call returns itself for continued tracing.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -334,7 +336,7 @@ def test_trace_call_returns_itself(tmp_path): # Tests for TracerState.close() -def test_tracer_state_close(tmp_path): +def test_tracer_state_close(tmp_path: Path) -> None: """Test that close() closes the trace file.""" trace_file = tmp_path / "test.tsv" tracer = TracerState(str(trace_file)) @@ -348,7 +350,7 @@ def test_tracer_state_close(tmp_path): # Tests for settrace() -def test_settrace_creates_tracer(tmp_path): +def test_settrace_creates_tracer(tmp_path: Path) -> None: """Test that settrace creates and returns a TracerState.""" trace_file = tmp_path / "test.tsv" tracer = settrace(str(trace_file)) @@ -361,7 +363,7 @@ def test_settrace_creates_tracer(tmp_path): tracer.close() -def test_settrace_enables_tracing(tmp_path): +def test_settrace_enables_tracing(tmp_path: Path) -> None: """Test that settrace enables sys.settrace.""" trace_file = tmp_path / "test.tsv" tracer = settrace(str(trace_file)) @@ -374,7 +376,9 @@ def test_settrace_enables_tracing(tmp_path): tracer.close() -def test_settrace_default_filename(tmp_path, monkeypatch): +def test_settrace_default_filename( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test that settrace uses default filename.""" monkeypatch.chdir(tmp_path) tracer = settrace() @@ -390,7 +394,7 @@ def test_settrace_default_filename(tmp_path, monkeypatch): # Tests for untrace() -def test_untrace_disables_tracing(tmp_path): +def test_untrace_disables_tracing(tmp_path: Path) -> None: """Test that untrace disables sys.settrace.""" trace_file = tmp_path / "test.tsv" tracer = settrace(str(trace_file)) @@ -401,7 +405,7 @@ def test_untrace_disables_tracing(tmp_path): assert sys.gettrace() is None -def test_untrace_closes_file(tmp_path): +def test_untrace_closes_file(tmp_path: Path) -> None: """Test that untrace closes the trace file.""" trace_file = tmp_path / "test.tsv" tracer = settrace(str(trace_file)) @@ -414,7 +418,7 @@ def test_untrace_closes_file(tmp_path): # Tests for main() - script execution -def test_main_traces_script(tmp_path): +def test_main_traces_script(tmp_path: Path) -> None: """Test main traces a Python script.""" # Create a simple test script script_file = tmp_path / "test_script.py" @@ -450,7 +454,7 @@ def simple_function(): assert "entry\tevent\tlevel\tfunction\tvalue\textra" in content -def test_main_with_nonexistent_script(tmp_path): +def test_main_with_nonexistent_script(tmp_path: Path) -> None: """Test the main CLI with a nonexistent script.""" trace_file = tmp_path / "trace_output.txt" @@ -472,7 +476,7 @@ def test_main_with_nonexistent_script(tmp_path): assert match_wrapped(result.stdout, "not found") -def test_main_module_mode_with_cli_runner(tmp_path): +def test_main_module_mode_with_cli_runner(tmp_path: Path) -> None: """Test the main CLI in module mode with subprocess.""" trace_file = tmp_path / "trace_output.txt" @@ -495,7 +499,7 @@ def test_main_module_mode_with_cli_runner(tmp_path): assert trace_file.stat().st_size > 0 -def test_main_module_tracing(tmp_path): +def test_main_module_tracing(tmp_path: Path) -> None: """Test main with module tracing (all module-related scenarios). Uses subprocess to avoid Click CliRunner I/O conflicts with sys.settrace() @@ -571,7 +575,9 @@ def test_main_module_tracing(tmp_path): assert trace_file3.exists() -def test_main_default_output_filename(tmp_path, monkeypatch): +def test_main_default_output_filename( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Test main uses default output filename.""" monkeypatch.chdir(tmp_path) @@ -593,7 +599,7 @@ def test_main_default_output_filename(tmp_path, monkeypatch): assert Path("trace.tsv").exists() -def test_main_short_output_option(tmp_path): +def test_main_short_output_option(tmp_path: Path) -> None: """Test main with -o short option.""" script_file = tmp_path / "test_script.py" script_file.write_text("x = 1\n") @@ -616,7 +622,7 @@ def test_main_short_output_option(tmp_path): assert trace_file.exists() -def test_main_handles_script_with_system_exit(tmp_path): +def test_main_handles_script_with_system_exit(tmp_path: Path) -> None: """Test main handles scripts that call sys.exit().""" script_file = tmp_path / "exit_script.py" script_file.write_text("import sys\nsys.exit(0)\n") @@ -640,7 +646,7 @@ def test_main_handles_script_with_system_exit(tmp_path): assert trace_file.exists() -def test_main_with_subprocess(tmp_path): +def test_main_with_subprocess(tmp_path: Path) -> None: """Test that the script can be executed directly via subprocess. This test verifies that the __main__ block works correctly. @@ -663,7 +669,7 @@ def test_main_with_subprocess(tmp_path): assert trace_file.exists() -def test_main_with_oserror_in_script(tmp_path): +def test_main_with_oserror_in_script(tmp_path: Path) -> None: """Test main handles OSError during script execution.""" script_file = tmp_path / "error_script.py" script_file.write_text("raise OSError('Test error')\n") @@ -687,7 +693,7 @@ def test_main_with_oserror_in_script(tmp_path): assert trace_file.exists() -def test_main_with_runtime_error_in_script(tmp_path): +def test_main_with_runtime_error_in_script(tmp_path: Path) -> None: """Test main handles RuntimeError during script execution.""" script_file = tmp_path / "error_script.py" script_file.write_text("raise RuntimeError('Test error')\n") @@ -711,7 +717,7 @@ def test_main_with_runtime_error_in_script(tmp_path): assert trace_file.exists() -def test_main_with_value_error_in_script(tmp_path): +def test_main_with_value_error_in_script(tmp_path: Path) -> None: """Test main handles ValueError during script execution.""" script_file = tmp_path / "error_script.py" script_file.write_text("raise ValueError('Test error')\n") @@ -738,7 +744,7 @@ def test_main_with_value_error_in_script(tmp_path): # Integration tests -def test_full_tracing_workflow(tmp_path): +def test_full_tracing_workflow(tmp_path: Path) -> None: """Test complete tracing workflow with actual function execution.""" trace_file = tmp_path / "trace.tsv" @@ -747,10 +753,10 @@ def test_full_tracing_workflow(tmp_path): try: # Execute some code to trace - def add(a, b): + def add(a: int, b: int) -> int: return a + b - def multiply(x, y): + def multiply(x: int, y: int) -> int: return x * y result1 = add(2, 3) @@ -775,15 +781,15 @@ def multiply(x, y): assert len(lines) > 1 # Header + at least some trace entries -def test_tracing_with_nested_calls(tmp_path): +def test_tracing_with_nested_calls(tmp_path: Path) -> None: """Test tracing with nested function calls.""" trace_file = tmp_path / "trace.tsv" tracer = settrace(str(trace_file)) try: - def outer(): - def inner(): + def outer() -> str: + def inner() -> str: return "inner" return inner() @@ -800,7 +806,7 @@ def inner(): assert "outer" in content or "inner" in content -def test_cli_produces_valid_tsv(tmp_path): +def test_cli_produces_valid_tsv(tmp_path: Path) -> None: """Test that CLI produces valid TSV file.""" script_file = tmp_path / "test_script.py" script_file.write_text(""" @@ -844,7 +850,9 @@ def func(): # Direct main() function tests for coverage -def test_main_function_with_script(tmp_path, capsys): +def test_main_function_with_script( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function directly with a script file.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -863,7 +871,9 @@ def test_main_function_with_script(tmp_path, capsys): assert "Trace complete" in captured.out -def test_main_function_with_nonexistent_script(tmp_path, capsys): +def test_main_function_with_nonexistent_script( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with nonexistent script.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -880,7 +890,9 @@ def test_main_function_with_nonexistent_script(tmp_path, capsys): assert "not found" in captured.out -def test_main_function_with_module(tmp_path, capsys): +def test_main_function_with_module( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function in module mode.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -896,7 +908,9 @@ def test_main_function_with_module(tmp_path, capsys): assert "Trace complete" in captured.out -def test_main_function_with_invalid_module(tmp_path, capsys): +def test_main_function_with_invalid_module( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with invalid module name.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -912,7 +926,9 @@ def test_main_function_with_invalid_module(tmp_path, capsys): assert trace_file.exists() -def test_main_function_with_script_system_exit(tmp_path, capsys): +def test_main_function_with_script_system_exit( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with script that calls sys.exit().""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -928,7 +944,9 @@ def test_main_function_with_script_system_exit(tmp_path, capsys): assert trace_file.exists() -def test_main_function_with_script_import_error(tmp_path, capsys): +def test_main_function_with_script_import_error( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with script that has import error.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel @@ -945,7 +963,9 @@ def test_main_function_with_script_import_error(tmp_path, capsys): assert trace_file.exists() -def test_main_function_with_script_value_error(tmp_path, capsys): +def test_main_function_with_script_value_error( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: """Test main function with script that raises ValueError.""" from firecrown.fctools.tracer import main # pylint: disable=import-outside-toplevel diff --git a/tests/generators/test_inferred_galaxy_zdist.py b/tests/generators/test_inferred_galaxy_zdist.py index 6c524b58f..2b213ba68 100644 --- a/tests/generators/test_inferred_galaxy_zdist.py +++ b/tests/generators/test_inferred_galaxy_zdist.py @@ -3,7 +3,6 @@ import copy import re from itertools import pairwise, product -from typing import Any import numpy as np import numpy.typing as npt @@ -14,6 +13,7 @@ from scipy.integrate import simpson from firecrown.generators._inferred_galaxy_zdist import ( + BinsType, LSST_Y1_LENS_HARMONIC_BIN_COLLECTION, LSST_Y1_SOURCE_HARMONIC_BIN_COLLECTION, LSST_Y10_LENS_HARMONIC_BIN_COLLECTION, @@ -60,7 +60,7 @@ ) ], ) -def fixture_zdist_y1(request): +def fixture_zdist_y1(request: pytest.FixtureRequest) -> ZDistLSSTSRD: """Fixture for the ZDistLSSTSRD class.""" return request.param[0]( use_autoknot=request.param[1], autoknots_reltol=request.param[2] @@ -68,7 +68,7 @@ def fixture_zdist_y1(request): @pytest.fixture(name="z_array", params=[100, 600, 1000]) -def fixture_z_array(request): +def fixture_z_array(request: pytest.FixtureRequest) -> npt.NDArray[np.float64]: """Fixture for the z array.""" return np.linspace(0, 3.0, request.param) @@ -76,13 +76,13 @@ def fixture_z_array(request): @pytest.fixture( name="bins", params=[Y1_LENS_BINS, Y10_LENS_BINS, Y1_SOURCE_BINS, Y10_SOURCE_BINS] ) -def fixture_bins(request) -> dict[str, Any]: +def fixture_bins(request: pytest.FixtureRequest) -> BinsType: """Fixture for the bins.""" return request.param @pytest.fixture(name="reltol", params=[1e-4, 1e-5]) -def fixture_reltol(request): +def fixture_reltol(request: pytest.FixtureRequest) -> float: """Fixture for the relative tolerance.""" return request.param @@ -109,7 +109,9 @@ def fixture_reltol(request): ), ids=[f"{b[0]}_autoknots_{b[1]}" for b in product(BINS_LIST, [True, False])], ) -def fixture_zdist_bins(request) -> list[ZDistLSSTSRDBin]: +def fixture_zdist_bins( + request: pytest.FixtureRequest, +) -> list[ZDistLSSTSRDBin]: """Fixture for the ZDistLSSTSRD class.""" match request.param[0]: case "one_lens_y1": @@ -140,7 +142,7 @@ def fixture_zdist_bins(request) -> list[ZDistLSSTSRDBin]: return bins -def test_zdist(zdist: ZDistLSSTSRD, z_array: npt.NDArray[np.float64]): +def test_zdist(zdist: ZDistLSSTSRD, z_array: npt.NDArray[np.float64]) -> None: """Test the ZDistLSSTSRD class.""" Pz = zdist.distribution(z_array) assert Pz.shape == z_array.shape @@ -149,8 +151,8 @@ def test_zdist(zdist: ZDistLSSTSRD, z_array: npt.NDArray[np.float64]): def test_compute_one_bin_dist( zdist: ZDistLSSTSRD, z_array: npt.NDArray[np.float64], - bins: dict[str, Any], -): + bins: BinsType, +) -> None: """Test the compute_binned_dist method.""" Pz = zdist.binned_distribution( @@ -170,8 +172,8 @@ def test_compute_one_bin_dist( def test_compute_all_bins_dist( zdist: ZDistLSSTSRD, z_array: npt.NDArray[np.float64], - bins: dict[str, Any], -): + bins: BinsType, +) -> None: """Test the compute_binned_dist method.""" for zpl, zpu in pairwise(bins["edges"]): @@ -188,7 +190,7 @@ def test_compute_all_bins_dist( assert_allclose(simpson(y=Pz.dndz, x=Pz.z), 1.0, atol=1e-3) -def test_zdist_bin(): +def test_zdist_bin() -> None: """Test the ZDistLSSTSRDBin class.""" zpl = 0.1 zpu = 0.2 @@ -214,7 +216,7 @@ def test_zdist_bin(): assert zbin.measurements == measurements -def test_zdist_bin_generate(zdist: ZDistLSSTSRD): +def test_zdist_bin_generate(zdist: ZDistLSSTSRD) -> None: """Test the ZDistLSSTSRDBin generate method.""" zpl = 0.1 zpu = 0.2 @@ -240,7 +242,7 @@ def test_zdist_bin_generate(zdist: ZDistLSSTSRD): assert Pz.measurements == measurements -def test_zdist_bin_from_bad_yaml(): +def test_zdist_bin_from_bad_yaml() -> None: """Test that the right exception is thrown if the yaml is malformed.""" bin_yaml = """ zpl: 0.1 @@ -260,7 +262,7 @@ def test_zdist_bin_from_bad_yaml(): _ = base_model_from_yaml(ZDistLSSTSRDBin, bin_yaml) -def test_zdist_bin_from_yaml(): +def test_zdist_bin_from_yaml() -> None: """Test the ZDistLSSTSRDBin class from_json method.""" zpl = 0.1 zpu = 0.2 @@ -291,7 +293,7 @@ def test_zdist_bin_from_yaml(): assert zbin.measurements == measurements -def test_zdist_bin_to_yaml(): +def test_zdist_bin_to_yaml() -> None: """Test the ZDistLSSTSRDBin class to_yaml method.""" zbin = ZDistLSSTSRDBin( zpl=0.1, @@ -317,7 +319,7 @@ def test_zdist_bin_to_yaml(): } -def test_zdist_bin_collection(zdist_bins): +def test_zdist_bin_collection(zdist_bins: list[ZDistLSSTSRDBin]) -> None: """Test the ZDistLSSTSRDBinCollection class.""" alpha = 0.9 beta = 2.0 @@ -333,7 +335,9 @@ def test_zdist_bin_collection(zdist_bins): assert zbin_collection.bins == zdist_bins -def test_zdist_bin_collection_generate(zdist_bins): +def test_zdist_bin_collection_generate( + zdist_bins: list[ZDistLSSTSRDBin], +) -> None: """Test the ZDistLSSTSRDBinCollection generate method.""" alpha = 0.9 beta = 2.0 @@ -361,7 +365,9 @@ def test_zdist_bin_collection_generate(zdist_bins): assert Pz.measurements == zbin.measurements -def test_zdist_bin_collection_to_yaml(zdist_bins): +def test_zdist_bin_collection_to_yaml( + zdist_bins: list[ZDistLSSTSRDBin], +) -> None: """Test the ZDistLSSTSRDBinCollection to_yaml method.""" alpha = 0.9 beta = 2.0 @@ -398,7 +404,9 @@ def test_zdist_bin_collection_to_yaml(zdist_bins): } -def test_zdist_bin_collection_from_yaml(zdist_bins): +def test_zdist_bin_collection_from_yaml( + zdist_bins: list[ZDistLSSTSRDBin], +) -> None: """Test the ZDistLSSTSRDBinCollection from_yaml method.""" alpha = 0.9 beta = 2.0 @@ -437,7 +445,7 @@ def test_zdist_bin_collection_from_yaml(zdist_bins): assert zbin.measurements == zbin_from_yaml.measurements -def test_make_measurement_from_measurement(): +def test_make_measurement_from_measurement() -> None: cluster_meas: set[Measurement] = {Clusters.COUNTS} galaxy_meas: set[Measurement] = {Galaxies.SHEAR_E} cmb_meas: set[Measurement] = {CMB.CONVERGENCE} @@ -446,7 +454,7 @@ def test_make_measurement_from_measurement(): assert make_measurements(cmb_meas) == cmb_meas -def test_make_measurement_from_dictionary(): +def test_make_measurement_from_dictionary() -> None: cluster_info = [{"subject": "Clusters", "property": "COUNTS"}] galaxy_info = [{"subject": "Galaxies", "property": "SHEAR_E"}] cmb_info = [{"subject": "CMB", "property": "CONVERGENCE"}] @@ -471,7 +479,7 @@ def test_make_measurement_from_dictionary(): _ = make_measurements({3}) # type: ignore -def test_distribution(zdist: ZDistLSSTSRD): +def test_distribution(zdist: ZDistLSSTSRD) -> None: """Test the distribution method.""" stats = zdist.compute_distribution(0.03) @@ -484,7 +492,7 @@ def test_distribution(zdist: ZDistLSSTSRD): assert_allclose([0.0, middle_z, zdist.max_z], bins) -def test_true_distribution(zdist: ZDistLSSTSRD): +def test_true_distribution(zdist: ZDistLSSTSRD) -> None: """Test the true_distribution method.""" stats = zdist.compute_true_distribution() assert isinstance(stats, Ncm.StatsDist1d) diff --git a/tests/integration/models/two_point/test_theory.py b/tests/integration/models/two_point/test_theory.py index ab09cb273..80e0e9529 100644 --- a/tests/integration/models/two_point/test_theory.py +++ b/tests/integration/models/two_point/test_theory.py @@ -1,8 +1,10 @@ """Integration tests for the TwoPointTheory class using real Source instances.""" import pytest +import sacc -from firecrown.likelihood import Source +from firecrown.likelihood import Source, Tracer +from firecrown.modeling_tools import ModelingTools from firecrown.models.two_point import TwoPointTheory from firecrown.updatable import ParamsMap @@ -10,24 +12,24 @@ class FakeSource(Source): """A minimal concrete Source implementation for testing.""" - def read_systematics(self, sacc_data) -> None: + def read_systematics(self, sacc_data: sacc.Sacc) -> None: pass - def _read(self, sacc_data) -> None: + def _read(self, sacc_data: sacc.Sacc) -> None: pass - def _update_source(self, params) -> None: + def _update_source(self, params: ParamsMap) -> None: pass def get_scale(self) -> float: return 1.0 - def create_tracers(self, tools): - return [] + def create_tracers(self, tools: ModelingTools) -> tuple[list[Tracer], object]: + raise NotImplementedError("FakeSource only supports update and reset tests.") @pytest.mark.integration -def test_two_point_theory_update_integration(): +def test_two_point_theory_update_integration() -> None: """Integration test: update method updates real sources.""" source1 = FakeSource("tracer_1") source2 = FakeSource("tracer_2") @@ -46,7 +48,7 @@ def test_two_point_theory_update_integration(): @pytest.mark.integration -def test_two_point_theory_reset_integration(): +def test_two_point_theory_reset_integration() -> None: """Integration test: reset method resets real sources.""" source1 = FakeSource("tracer_1") source2 = FakeSource("tracer_2") diff --git a/tests/likelihood/gauss_family/lkscript_const_gaussian.py b/tests/likelihood/gauss_family/lkscript_const_gaussian.py index 5c9c3a2f2..cdc57b337 100644 --- a/tests/likelihood/gauss_family/lkscript_const_gaussian.py +++ b/tests/likelihood/gauss_family/lkscript_const_gaussian.py @@ -6,6 +6,7 @@ import sacc from firecrown.likelihood._gaussian import ConstGaussian +from firecrown.likelihood._likelihood import NamedParameters from firecrown.likelihood.supernova._supernova import Supernova from firecrown.modeling_tools import ( CCLFactory, @@ -14,7 +15,7 @@ ) -def build_likelihood(_): +def build_likelihood(_: NamedParameters) -> tuple[ConstGaussian, ModelingTools]: """Return a ConstGaussian (likelihood) object.""" statistic = Supernova(sacc_tracer="pantheon") likelihood = ConstGaussian(statistics=[statistic]) diff --git a/tests/likelihood/gauss_family/lkscript_two_point.py b/tests/likelihood/gauss_family/lkscript_two_point.py index 6aa898d2c..f53dacfd1 100644 --- a/tests/likelihood/gauss_family/lkscript_two_point.py +++ b/tests/likelihood/gauss_family/lkscript_two_point.py @@ -19,7 +19,7 @@ ) -def build_likelihood(params: NamedParameters): +def build_likelihood(params: NamedParameters) -> tuple[ConstGaussian, ModelingTools]: """Return a ConstGaussian (likelihood) object.""" sacc_data = sacc.Sacc() diff --git a/tests/likelihood/gauss_family/lkscript_two_point_pure_ccl.py b/tests/likelihood/gauss_family/lkscript_two_point_pure_ccl.py index d54d8ca87..3c3352c7d 100644 --- a/tests/likelihood/gauss_family/lkscript_two_point_pure_ccl.py +++ b/tests/likelihood/gauss_family/lkscript_two_point_pure_ccl.py @@ -20,7 +20,7 @@ ) -def build_likelihood(params: NamedParameters): +def build_likelihood(params: NamedParameters) -> tuple[ConstGaussian, ModelingTools]: """Return a ConstGaussian (likelihood) object.""" sacc_data = sacc.Sacc() diff --git a/tests/likelihood/gauss_family/statistic/source/test_source.py b/tests/likelihood/gauss_family/statistic/source/test_source.py index 5cb6dcf0d..624f5b64b 100644 --- a/tests/likelihood/gauss_family/statistic/source/test_source.py +++ b/tests/likelihood/gauss_family/statistic/source/test_source.py @@ -5,7 +5,7 @@ # pylint: disable=too-many-locals import itertools as it -from typing import Callable, List +from typing import Callable, List, Sequence import numpy as np import numpy.typing as npt @@ -30,6 +30,7 @@ from firecrown.likelihood.number_counts._args import NumberCountsArgs from firecrown.likelihood.weak_lensing import WeakLensingArgs from firecrown.metadata_functions import extract_all_tracers_tomographic_bins +from firecrown.metadata_types import TomographicBin from firecrown.modeling_tools import ModelingTools from firecrown.updatable import ParamsMap, get_default_params @@ -53,7 +54,9 @@ "ConstantMagnificationBiasSystematicFactory", ], ) -def fixture_nc_sys_factory(request) -> nc_factories.NumberCountsSystematicFactory: +def fixture_nc_sys_factory( + request: pytest.FixtureRequest, +) -> nc_factories.NumberCountsSystematicFactory: """Fixture for the NumberCountsSystematicFactory class.""" return request.param @@ -77,7 +80,9 @@ def fixture_nc_sys_factory(request) -> nc_factories.NumberCountsSystematicFactor "PhotoZShiftandStretchFactory", ], ) -def fixture_wl_sys_factory(request): +def fixture_wl_sys_factory( + request: pytest.FixtureRequest, +) -> wl.WeakLensingSystematicFactory: """Fixture for the WeakLensingSystematicFactory class.""" return request.param @@ -89,16 +94,16 @@ class TrivialSourceGalaxyArgs(SourceGalaxyArgs): class TrivialSourceGalaxy(SourceGalaxy[TrivialSourceGalaxyArgs]): """This is the most trivial possible subclass of SourceGalaxy.""" - def create_tracers(self, tools: ModelingTools): - return Tracer(pyccl.Tracer()), TrivialSourceGalaxyArgs( - z=np.array([]), dndz=np.array([]) + def create_tracers(self, tools: ModelingTools) -> tuple[Sequence[Tracer], object]: + raise NotImplementedError( + "TrivialSourceGalaxy only supports source argument tests." ) def get_scale(self) -> float: return 1.0 -def test_trivial_tracer_construction(empty_pyccl_tracer): +def test_trivial_tracer_construction(empty_pyccl_tracer: pyccl.Tracer) -> None: trivial = Tracer(empty_pyccl_tracer) assert trivial.ccl_tracer is empty_pyccl_tracer # If we have not assigned a name through the initializer, the name of the resulting @@ -112,7 +117,7 @@ def test_trivial_tracer_construction(empty_pyccl_tracer): assert not trivial.has_pt -def test_tracer_construction_with_name(empty_pyccl_tracer): +def test_tracer_construction_with_name(empty_pyccl_tracer: pyccl.Tracer) -> None: named = Tracer(empty_pyccl_tracer, tracer_name="Fred") assert named.ccl_tracer is empty_pyccl_tracer assert named.tracer_name == "Fred" @@ -121,7 +126,7 @@ def test_tracer_construction_with_name(empty_pyccl_tracer): assert not named.has_pt -def test_nc_linear_bias_systematic(): +def test_nc_linear_bias_systematic() -> None: a = nc_sys.LinearBiasSystematic("xxx") assert isinstance(a, nc_sys.LinearBiasSystematic) assert a.parameter_prefix == "xxx" @@ -146,7 +151,7 @@ def test_nc_linear_bias_systematic(): def test_nc_nonlinear_bias_systematic_tracer_args_missing( tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: a = nc_sys.LinearBiasSystematic("xxx") # The values in the ParamsMap and the tracer_args are set to allow easy verification # that a tracer_args of None is handled correctly. @@ -157,7 +162,7 @@ def test_nc_nonlinear_bias_systematic_tracer_args_missing( assert np.allclose(new_tracer_args.bias, np.array([1.0])) -def test_trivial_source_galaxy_construction(): +def test_trivial_source_galaxy_construction() -> None: trivial = TrivialSourceGalaxy(sacc_tracer="no-sacc-tracer") with pytest.raises( @@ -167,7 +172,7 @@ def test_trivial_source_galaxy_construction(): trivial.read(sacc.Sacc()) -def test_trivial_source_select_field(): +def test_trivial_source_select_field() -> None: tools = ModelingTools() trivial = TrivialSourceGalaxy(sacc_tracer="no-sacc-tracer") select_field: SourceGalaxySelectField[SourceGalaxyArgs] = SourceGalaxySelectField( @@ -183,15 +188,16 @@ def test_trivial_source_select_field(): def test_weak_lensing_source_init( - sacc_galaxy_cells_src0_src0, tools_with_vanilla_cosmology: ModelingTools -): - sacc_data, z, dndz = sacc_galaxy_cells_src0_src0 + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], + tools_with_vanilla_cosmology: ModelingTools, +) -> None: + sacc_data, expected_bin = sacc_galaxy_cells_src0_src0 source = wl.WeakLensing(sacc_tracer="src0") source.read(sacc_data) - assert_allclose(source.tracer_args.z, z) - assert_allclose(source.tracer_args.dndz, dndz) + assert_allclose(source.tracer_args.z, expected_bin.z) + assert_allclose(source.tracer_args.dndz, expected_bin.dndz) source.update(ParamsMap()) ts = source.get_tracers(tools_with_vanilla_cosmology) assert len(ts) == 1 @@ -200,8 +206,10 @@ def test_weak_lensing_source_init( assert len(dp) == 0 -def test_weak_lensing_source_create_ready(sacc_galaxy_cells_src0_src0): - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 +def test_weak_lensing_source_create_ready( + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) src0 = next((obj for obj in all_tracers if obj.bin_name == "src0"), None) @@ -216,8 +224,10 @@ def test_weak_lensing_source_create_ready(sacc_galaxy_cells_src0_src0): assert_allclose(source_ready.tracer_args.dndz, source_read.tracer_args.dndz) -def test_weak_lensing_source_factory(sacc_galaxy_cells_src0_src0): - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 +def test_weak_lensing_source_factory( + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) src0 = next((obj for obj in all_tracers if obj.bin_name == "src0"), None) @@ -233,8 +243,10 @@ def test_weak_lensing_source_factory(sacc_galaxy_cells_src0_src0): assert_allclose(source_ready.tracer_args.dndz, source_read.tracer_args.dndz) -def test_weak_lensing_source_factory_cache(sacc_galaxy_cells_src0_src0): - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 +def test_weak_lensing_source_factory_cache( + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) src0 = next((obj for obj in all_tracers if obj.bin_name == "src0"), None) @@ -248,9 +260,10 @@ def test_weak_lensing_source_factory_cache(sacc_galaxy_cells_src0_src0): @pytest.mark.parametrize("include_z_dependence", [True, False]) def test_weak_lensing_source_factory_global_systematics( - sacc_galaxy_cells_src0_src0, include_z_dependence -): - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], + include_z_dependence: bool, +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) src0 = next((obj for obj in all_tracers if obj.bin_name == "src0"), None) @@ -279,8 +292,10 @@ def test_weak_lensing_source_factory_global_systematics( assert source_ready.systematics == source_read.systematics -def test_weak_lensing_source_init_wrong_name(sacc_galaxy_cells_src0_src0): - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 +def test_weak_lensing_source_init_wrong_name( + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0 source = wl.WeakLensing(sacc_tracer="src10") with pytest.raises(KeyError, match="src10"): @@ -288,16 +303,17 @@ def test_weak_lensing_source_init_wrong_name(sacc_galaxy_cells_src0_src0): def test_number_counts_source_init( - sacc_galaxy_cells_lens0_lens0, tools_with_vanilla_cosmology: ModelingTools -): + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], + tools_with_vanilla_cosmology: ModelingTools, +) -> None: - sacc_data, z, dndz = sacc_galaxy_cells_lens0_lens0 + sacc_data, expected_bin = sacc_galaxy_cells_lens0_lens0 source = nc.NumberCounts(sacc_tracer="lens0") source.read(sacc_data) - assert_allclose(source.tracer_args.z, z) - assert_allclose(source.tracer_args.dndz, dndz) + assert_allclose(source.tracer_args.z, expected_bin.z) + assert_allclose(source.tracer_args.dndz, expected_bin.dndz) source.update(ParamsMap(lens0_bias=1.1)) dp = source.get_derived_parameters() assert dp is not None @@ -306,8 +322,10 @@ def test_number_counts_source_init( assert len(ts) == 1 -def test_number_counts_source_create_ready(sacc_galaxy_cells_lens0_lens0): - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 +def test_number_counts_source_create_ready( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) lens0 = next((obj for obj in all_tracers if obj.bin_name == "lens0"), None) @@ -322,8 +340,10 @@ def test_number_counts_source_create_ready(sacc_galaxy_cells_lens0_lens0): assert_allclose(source_ready.tracer_args.dndz, source_read.tracer_args.dndz) -def test_number_counts_source_factory(sacc_galaxy_cells_lens0_lens0): - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 +def test_number_counts_source_factory( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) lens0 = next((obj for obj in all_tracers if obj.bin_name == "lens0"), None) @@ -339,8 +359,10 @@ def test_number_counts_source_factory(sacc_galaxy_cells_lens0_lens0): assert_allclose(source_ready.tracer_args.dndz, source_read.tracer_args.dndz) -def test_number_counts_source_factory_cache(sacc_galaxy_cells_lens0_lens0): - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 +def test_number_counts_source_factory_cache( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) lens0 = next((obj for obj in all_tracers if obj.bin_name == "lens0"), None) @@ -352,8 +374,10 @@ def test_number_counts_source_factory_cache(sacc_galaxy_cells_lens0_lens0): assert source_ready is nc_factory.create(tomographic_bin=lens0) -def test_number_counts_source_factory_global_systematics(sacc_galaxy_cells_lens0_lens0): - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 +def test_number_counts_source_factory_global_systematics( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 all_tracers = extract_all_tracers_tomographic_bins(sacc_data) lens0 = next((obj for obj in all_tracers if obj.bin_name == "lens0"), None) @@ -378,8 +402,10 @@ def test_number_counts_source_factory_global_systematics(sacc_galaxy_cells_lens0 assert source_ready.systematics == source_read.systematics -def test_number_counts_source_init_wrong_name(sacc_galaxy_cells_lens0_lens0): - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 +def test_number_counts_source_init_wrong_name( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 source = nc.NumberCounts(sacc_tracer="lens10") with pytest.raises(KeyError, match="lens10"): @@ -388,48 +414,48 @@ def test_number_counts_source_init_wrong_name(sacc_galaxy_cells_lens0_lens0): def test_number_counts_systematic_factory( nc_sys_factory: nc_factories.NumberCountsSystematicFactory, -): +) -> None: sys_pz_shift = nc_sys_factory.create("bin_1") assert sys_pz_shift.parameter_prefix == "bin_1" -def test_wl_photozshiftfactory_no_globals(): +def test_wl_photozshiftfactory_no_globals() -> None: factory = wl.PhotoZShiftFactory() with pytest.raises(ValueError, match="PhotoZShift cannot be global"): _ = factory.create_global() -def test_wl_photozshiftandstretchfactory_no_globals(): +def test_wl_photozshiftandstretchfactory_no_globals() -> None: factory = wl.PhotoZShiftandStretchFactory() with pytest.raises(ValueError, match="PhotoZShiftandStretch cannot be global"): _ = factory.create_global() -def test_wl_multiplicativeshearbiasfactory_no_globals(): +def test_wl_multiplicativeshearbiasfactory_no_globals() -> None: factory = wl.MultiplicativeShearBiasFactory() with pytest.raises(ValueError, match="MultiplicativeShearBias cannot be global"): _ = factory.create_global() -def test_nc_photozshiftfactory_no_globals(): +def test_nc_photozshiftfactory_no_globals() -> None: factory = nc_factories.PhotoZShiftFactory() with pytest.raises(ValueError, match="PhotoZShift cannot be global"): _ = factory.create_global() -def test_nc_photozshiftandstretchfactory_no_globals(): +def test_nc_photozshiftandstretchfactory_no_globals() -> None: factory = nc_factories.PhotoZShiftandStretchFactory() with pytest.raises(ValueError, match="PhotoZShiftandStretch cannot be global"): _ = factory.create_global() -def test_nc_linearbiassystematicfactory_no_globals(): +def test_nc_linearbiassystematicfactory_no_globals() -> None: factory = nc_factories.LinearBiasSystematicFactory() with pytest.raises(ValueError, match="LinearBiasSystematic cannot be global"): _ = factory.create_global() -def test_nc_magnificationbiassystematicfactory_no_globals(): +def test_nc_magnificationbiassystematicfactory_no_globals() -> None: factory = nc_factories.MagnificationBiasSystematicFactory() with pytest.raises( ValueError, match="MagnificationBiasSystematic cannot be global" @@ -437,7 +463,7 @@ def test_nc_magnificationbiassystematicfactory_no_globals(): _ = factory.create_global() -def test_nc_constantmagnificationbiassystematicfactory_no_globals(): +def test_nc_constantmagnificationbiassystematicfactory_no_globals() -> None: factory = nc_factories.ConstantMagnificationBiasSystematicFactory() with pytest.raises( ValueError, match="ConstantMagnificationBiasSystematic cannot be global" @@ -447,14 +473,14 @@ def test_nc_constantmagnificationbiassystematicfactory_no_globals(): def test_weak_lensing_systematic_factory( wl_sys_factory: wl.WeakLensingSystematicFactory, -): +) -> None: sys_pz_shift = wl_sys_factory.create("bin_1") assert sys_pz_shift.parameter_prefix == "bin_1" def test_wl_photozshiftandstretch_systematic( tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: a = wl.PhotoZShiftandStretch("xxx") assert isinstance(a, wl.PhotoZShiftandStretch) assert a.parameter_prefix == "xxx" @@ -489,7 +515,7 @@ def test_wl_photozshiftandstretch_systematic( def test_nc_photozshiftandstretch_systematic( tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: a = nc_sys.PhotoZShiftandStretch("xxx") assert isinstance(a, nc_sys.PhotoZShiftandStretch) assert a.parameter_prefix == "xxx" @@ -537,7 +563,7 @@ def test_dndz_shift_active( [npt.NDArray[np.float64], npt.NDArray[np.float64], float, float], tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], ], -): +) -> None: sigma = 0.05 mu = 0.5 shift = 0.1 @@ -563,7 +589,7 @@ def test_dndz_shift_active( assert_allclose(dndz_interp_vals, dndz_shifted_stretched, rtol=1.0e-6, atol=1.0e-6) -def test_dndz_shift_and_stretch_active_negative_sigma(): +def test_dndz_shift_and_stretch_active_negative_sigma() -> None: with pytest.raises(ValueError, match="Stretch Parameter must be positive"): dndz_shift_and_stretch_active( z=np.array([0.0, 0.1]), @@ -573,7 +599,7 @@ def test_dndz_shift_and_stretch_active_negative_sigma(): ) -def test_dndz_shift_and_stretch_passive_negative_sigma(): +def test_dndz_shift_and_stretch_passive_negative_sigma() -> None: with pytest.raises(ValueError, match="Stretch Parameter must be positive"): dndz_shift_and_stretch_passive( z=np.array([0.0, 0.1]), @@ -586,7 +612,7 @@ def test_dndz_shift_and_stretch_passive_negative_sigma(): @pytest.mark.parametrize("shift,active", it.product([0.0, 0.1, -0.1], [True, False])) def test_photoz_shift( tools_with_vanilla_cosmology: ModelingTools, shift: float, active: bool -): +) -> None: photoz_shift = nc_sys.PhotoZShift("John", active=active) photoz_shift.update(ParamsMap({"John_delta_z": shift})) sigma = 0.05 @@ -618,7 +644,7 @@ def test_photoz_shift_stretch( shift: float, stretch: float, active: bool, -): +) -> None: photoz_shift_stretch = nc_sys.PhotoZShiftandStretch("John", active=active) photoz_shift_stretch.update( ParamsMap({"John_delta_z": shift, "John_sigma_z": stretch}) @@ -650,7 +676,7 @@ def test_photoz_shift_stretch( def _check_tatt_alignment_systematic_zdep( sys: wl.TattAlignmentSystematic, include_z_dependence: bool -): +) -> None: """Check that the TattAlignmentSystematic has the expected z-dependence parameters. """ @@ -672,7 +698,7 @@ def _check_tatt_alignment_systematic_zdep( @pytest.mark.parametrize("include_z_dependence", [True, False]) -def test_tatt_alignment_systematics(include_z_dependence): +def test_tatt_alignment_systematics(include_z_dependence: bool) -> None: sys = wl.TattAlignmentSystematic(include_z_dependence=include_z_dependence) assert isinstance(sys, wl.TattAlignmentSystematic) _check_tatt_alignment_systematic_zdep( @@ -681,7 +707,7 @@ def test_tatt_alignment_systematics(include_z_dependence): @pytest.mark.parametrize("include_z_dependence", [True, False]) -def test_tatt_alignment_systematic_factory(include_z_dependence): +def test_tatt_alignment_systematic_factory(include_z_dependence: bool) -> None: factory = wl.TattAlignmentSystematicFactory( include_z_dependence=include_z_dependence ) diff --git a/tests/likelihood/gauss_family/statistic/test_binned_cluster_number_counts.py b/tests/likelihood/gauss_family/statistic/test_binned_cluster_number_counts.py index 8aba79adc..1a37593b4 100644 --- a/tests/likelihood/gauss_family/statistic/test_binned_cluster_number_counts.py +++ b/tests/likelihood/gauss_family/statistic/test_binned_cluster_number_counts.py @@ -4,6 +4,8 @@ import pytest import sacc from crow import ClusterAbundance, ClusterShearProfile, kernel, mass_proxy +from crow.cluster_modules.completeness_models import Completeness +from crow.cluster_modules.purity_models import Purity from crow.properties import ClusterProperty from crow.recipes.binned_exact import ExactBinnedClusterRecipe from crow.recipes.binned_grid import GridBinnedClusterRecipe @@ -17,7 +19,9 @@ from firecrown.updatable import get_default_params_map -def get_base_binned_exact(completeness, purity) -> ExactBinnedClusterRecipe: +def get_base_binned_exact( + completeness: Completeness | None, purity: Purity | None +) -> ExactBinnedClusterRecipe: pivot_mass, pivot_redshift = 14.625862906, 0.6 cluster_recipe = ExactBinnedClusterRecipe( cluster_theory=ClusterAbundance( @@ -41,7 +45,7 @@ def get_base_binned_exact(completeness, purity) -> ExactBinnedClusterRecipe: def get_base_binned_grid( - completeness, purity, is_delta_sigma + completeness: Completeness | None, purity: Purity | None, is_delta_sigma: bool ) -> GridBinnedClusterRecipe: pivot_mass, pivot_redshift = 14.625862906, 0.6 cluster_recipe = GridBinnedClusterRecipe( diff --git a/tests/likelihood/gauss_family/statistic/test_statistic.py b/tests/likelihood/gauss_family/statistic/test_statistic.py index daf0677cc..9d562570f 100644 --- a/tests/likelihood/gauss_family/statistic/test_statistic.py +++ b/tests/likelihood/gauss_family/statistic/test_statistic.py @@ -15,7 +15,7 @@ VECTOR_CLASSES = (firecrown.data_types.TheoryVector, firecrown.data_types.DataVector) -def test_vector_create(): +def test_vector_create() -> None: vals = np.random.random_sample((10,)) assert isinstance(vals, np.ndarray) assert vals.dtype == np.float64 @@ -27,7 +27,7 @@ def test_vector_create(): assert np.array_equal(vals, result) -def test_vector_from_list(): +def test_vector_from_list() -> None: vals = [1.5, 2.5, -3.0, 10.0] assert isinstance(vals, list) assert len(vals) == 4 @@ -39,7 +39,7 @@ def test_vector_from_list(): assert result[i] == val -def test_vector_slicing(): +def test_vector_slicing() -> None: for cls in VECTOR_CLASSES: vec = cls.create(np.random.random_sample((12,))) assert isinstance(vec, cls) @@ -48,7 +48,7 @@ def test_vector_slicing(): assert isinstance(middle_part, cls) -def test_vector_copying(): +def test_vector_copying() -> None: for cls in VECTOR_CLASSES: vec = cls.create(np.random.random_sample((12,))) assert isinstance(vec, cls) @@ -58,7 +58,7 @@ def test_vector_copying(): assert isinstance(vec_copy, cls) -def test_excplicit_vector_construction(): +def test_excplicit_vector_construction() -> None: for cls in VECTOR_CLASSES: vec = cls(shape=(4,), dtype=np.float64) assert isinstance(vec, cls) @@ -66,7 +66,7 @@ def test_excplicit_vector_construction(): assert vec.dtype == np.float64 -def test_ufunc_on_vector(): +def test_ufunc_on_vector() -> None: data = np.array([0.0, 0.25, 0.50]) expected = np.sin(data) for cls in VECTOR_CLASSES: @@ -76,7 +76,7 @@ def test_ufunc_on_vector(): assert np.array_equal(result, expected) -def test_vector_residuals(): +def test_vector_residuals() -> None: theory = firecrown.data_types.TheoryVector.from_list([1.0, 2.0, 3.0]) data = firecrown.data_types.DataVector.from_list([1.1, 2.1, 3.1]) difference = firecrown.data_types.residuals(data, theory) @@ -87,7 +87,7 @@ def test_vector_residuals(): def test_guarded_statistic_read_only_once( sacc_data_for_trivial_stat: sacc.Sacc, trivial_stats: list[stat.TrivialStatistic] -): +) -> None: gs = stat.GuardedStatistic(trivial_stats.pop()) assert not gs.statistic.ready gs.read(sacc_data_for_trivial_stat) @@ -100,13 +100,15 @@ def test_guarded_statistic_read_only_once( def test_guarded_static_read_data_vector( sacc_data_for_trivial_stat: sacc.Sacc, trivial_stats: list[stat.TrivialStatistic] -): +) -> None: gs = stat.GuardedStatistic(trivial_stats.pop()) gs.read(sacc_data_for_trivial_stat) assert_allclose(gs.get_data_vector(), [1.0, 4.0, -3.0]) -def test_guarded_statistic_get_data_before_read(trivial_stats): +def test_guarded_statistic_get_data_before_read( + trivial_stats: list[stat.TrivialStatistic], +) -> None: s = trivial_stats.pop() with pytest.raises( stat.StatisticUnreadError, @@ -117,13 +119,13 @@ def test_guarded_statistic_get_data_before_read(trivial_stats): _ = g.get_data_vector() -def test_statistic_get_data_vector_before_read(): +def test_statistic_get_data_vector_before_read() -> None: s = stat.TrivialStatistic() with pytest.raises(AssertionError): _ = s.get_data_vector() -def test_guarded_statiistic_compute_theory_before_read(): +def test_guarded_statiistic_compute_theory_before_read() -> None: gs = stat.GuardedStatistic(stat.TrivialStatistic()) with pytest.raises( RuntimeError, @@ -132,13 +134,15 @@ def test_guarded_statiistic_compute_theory_before_read(): gs.compute_theory_vector(ModelingTools()) -def test_statistic_get_data_vector_after_read(sacc_data_for_trivial_stat): +def test_statistic_get_data_vector_after_read( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: s = stat.TrivialStatistic() s.read(sacc_data_for_trivial_stat) assert_allclose(s.get_data_vector(), [1.0, 4.0, -3.0]) -def test_statistic_get_theory_vector_before_compute(): +def test_statistic_get_theory_vector_before_compute() -> None: s = stat.TrivialStatistic() with pytest.raises( RuntimeError, match="The theory for statistic .* has not been computed yet\\." @@ -146,7 +150,9 @@ def test_statistic_get_theory_vector_before_compute(): _ = s.get_theory_vector() -def test_statistic_get_theory_vector_after_compute(sacc_data_for_trivial_stat): +def test_statistic_get_theory_vector_after_compute( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: s = stat.TrivialStatistic() s.read(sacc_data_for_trivial_stat) s.update(ParamsMap(mean=10.5)) @@ -154,7 +160,9 @@ def test_statistic_get_theory_vector_after_compute(sacc_data_for_trivial_stat): assert_allclose(s.get_theory_vector(), [10.5, 10.5, 10.5]) -def test_statistic_get_theory_vector_after_reset(sacc_data_for_trivial_stat): +def test_statistic_get_theory_vector_after_reset( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: s = stat.TrivialStatistic() s.read(sacc_data_for_trivial_stat) s.update(ParamsMap(mean=10.5)) @@ -166,7 +174,7 @@ def test_statistic_get_theory_vector_after_reset(sacc_data_for_trivial_stat): _ = s.get_theory_vector() -def test_statistic_compute_theory_vector_before_read(): +def test_statistic_compute_theory_vector_before_read() -> None: s = stat.TrivialStatistic() with pytest.raises( RuntimeError, @@ -175,7 +183,9 @@ def test_statistic_compute_theory_vector_before_read(): s.compute_theory_vector(ModelingTools()) -def test_statistic_compute_theory_vector_before_update(sacc_data_for_trivial_stat): +def test_statistic_compute_theory_vector_before_update( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: s = stat.TrivialStatistic() s.read(sacc_data_for_trivial_stat) with pytest.raises( @@ -185,7 +195,9 @@ def test_statistic_compute_theory_vector_before_update(sacc_data_for_trivial_sta s.compute_theory_vector(ModelingTools()) -def test_statistic_compute_theory_vector_after_update(sacc_data_for_trivial_stat): +def test_statistic_compute_theory_vector_after_update( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: s = stat.TrivialStatistic() s.read(sacc_data_for_trivial_stat) s.update(ParamsMap(mean=10.5)) diff --git a/tests/likelihood/gauss_family/statistic/test_two_point.py b/tests/likelihood/gauss_family/statistic/test_two_point.py index caf207cb7..d285e4b88 100644 --- a/tests/likelihood/gauss_family/statistic/test_two_point.py +++ b/tests/likelihood/gauss_family/statistic/test_two_point.py @@ -3,6 +3,7 @@ """ import re +from pathlib import Path from unittest.mock import MagicMock import numpy as np @@ -50,7 +51,7 @@ @pytest.fixture(name="include_rsd", params=[True, False], ids=["rsd", "no_rsd"]) -def fixture_include_rsd(request) -> bool: +def fixture_include_rsd(request: pytest.FixtureRequest) -> bool: """Return whether to include RSD in the test.""" return request.param @@ -90,18 +91,20 @@ def fixture_two_point_without_window( @pytest.fixture(name="apply_interp_when", params=list(tp.ApplyInterpolationWhen)) -def fixture_apply_interp_when(request) -> tp.ApplyInterpolationWhen: +def fixture_apply_interp_when( + request: pytest.FixtureRequest, +) -> tp.ApplyInterpolationWhen: return request.param @pytest.fixture(name="window_fixture", params=["with_window", "no_window"]) -def fixture_window_data(request): +def fixture_window_data(request: pytest.FixtureRequest) -> str: """Parameterized fixture for window vs no-window test data.""" return request.param @pytest.fixture(name="binning_type", params=["lin", "log"]) -def fixture_binning_type(request) -> str: +def fixture_binning_type(request: pytest.FixtureRequest) -> str: """Parameterized fixture for binning types.""" return request.param @@ -153,15 +156,15 @@ def test_tracer_names() -> None: def test_two_point_src0_src0_window_parameterized( window_fixture: str, - sacc_galaxy_cells_src0_src0_window, - sacc_galaxy_cells_src0_src0_no_window, + sacc_galaxy_cells_src0_src0_window: tuple[sacc.Sacc, TomographicBin], + sacc_galaxy_cells_src0_src0_no_window: tuple[sacc.Sacc, TomographicBin], ) -> None: """Test TwoPoint theory calculations are repeatable for both window scenarios.""" if window_fixture == "with_window": - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_window + sacc_data, _ = sacc_galaxy_cells_src0_src0_window expected_window = True else: # no_window - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_no_window + sacc_data, _ = sacc_galaxy_cells_src0_src0_no_window expected_window = False src0 = WeakLensing(sacc_tracer="src0") @@ -212,10 +215,11 @@ def test_two_point_generate_ell_or_theta() -> None: def test_two_point_src0_src0_no_data_binning( - sacc_galaxy_cells_src0_src0_no_data, binning_type: str + sacc_galaxy_cells_src0_src0_no_data: tuple[sacc.Sacc, TomographicBin], + binning_type: str, ) -> None: """Test TwoPoint with different binning types when no data is present.""" - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_no_data + sacc_data, _ = sacc_galaxy_cells_src0_src0_no_data src0 = WeakLensing(sacc_tracer="src0") @@ -243,12 +247,18 @@ def test_two_point_src0_src0_no_data_binning( assert all(statistic.ells <= 100) -def test_two_point_lens0_lens0_no_data(sacc_galaxy_xis_lens0_lens0_no_data) -> None: - sacc_data, _, _ = sacc_galaxy_xis_lens0_lens0_no_data +def test_two_point_lens0_lens0_generates_configured_thetas_without_measurements( + sacc_galaxy_lens0_no_two_point_data: sacc.Sacc, +) -> None: + """Generate a configured angular grid when correlation measurements are absent. - src0 = NumberCounts(sacc_tracer="lens0") + This tests statistic setup, not a physical correlation prediction. - ell_config: EllOrThetaConfig = { + :param sacc_galaxy_lens0_no_two_point_data: Redshift tracer without two-point data. + """ + lens0 = NumberCounts(sacc_tracer="lens0") + + theta_config: EllOrThetaConfig = { "minimum": 0.0, "maximum": 1.0, "n": 5, @@ -257,12 +267,12 @@ def test_two_point_lens0_lens0_no_data(sacc_galaxy_xis_lens0_lens0_no_data) -> N statistic = tp.TwoPoint( "galaxy_density_xi", - src0, - src0, - ell_or_theta=ell_config, + lens0, + lens0, + ell_or_theta=theta_config, ) with pytest.warns(UserWarning): - statistic.read(sacc_data) + statistic.read(sacc_galaxy_lens0_no_two_point_data) assert statistic.window is None assert statistic.ells is None @@ -277,7 +287,7 @@ def test_two_point_lens0_lens0_no_data(sacc_galaxy_xis_lens0_lens0_no_data) -> N maximum=floats(min_value=51, max_value=1000), n=integers(min_value=2, max_value=20), ) -def test_ell_generation_bounds_property(minimum: float, maximum: float, n: int): +def test_ell_generation_bounds_property(minimum: float, maximum: float, n: int) -> None: """Test that generated ells are always within specified bounds using hypothesis.""" # Skip invalid cases assume(minimum < maximum) @@ -306,7 +316,9 @@ def test_ell_generation_bounds_property(minimum: float, maximum: float, n: int): maximum=floats(min_value=0.6, max_value=2.0), n=integers(min_value=2, max_value=15), ) -def test_theta_generation_bounds_property(minimum: float, maximum: float, n: int): +def test_theta_generation_bounds_property( + minimum: float, maximum: float, n: int +) -> None: """Generated thetas are always within specified bounds using hypothesis.""" # Skip invalid cases assume(minimum < maximum) @@ -331,8 +343,10 @@ def test_two_point_theory_construction() -> None: assert theory.source1 is src0 -def test_two_point_lens0_lens0_config(sacc_galaxy_xis_lens0_lens0) -> None: - sacc_data, _, _ = sacc_galaxy_xis_lens0_lens0 +def test_two_point_lens0_lens0_config( + sacc_galaxy_xis_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_xis_lens0_lens0 src0 = NumberCounts(sacc_tracer="lens0") @@ -363,8 +377,10 @@ def test_two_point_lens0_lens0_config(sacc_galaxy_xis_lens0_lens0) -> None: statistic.compute_theory_vector(tools) -def test_two_point_src0_src0_no_data_error(sacc_galaxy_cells_src0_src0_no_data) -> None: - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_no_data +def test_two_point_src0_src0_no_data_error( + sacc_galaxy_cells_src0_src0_no_data: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_cells_src0_src0_no_data src0 = WeakLensing(sacc_tracer="src0") @@ -380,14 +396,16 @@ def test_two_point_src0_src0_no_data_error(sacc_galaxy_cells_src0_src0_no_data) statistic.read(sacc_data) -def test_two_point_lens0_lens0_no_data_error( - sacc_galaxy_xis_lens0_lens0_no_data, +def test_two_point_lens0_lens0_requires_theta_config_without_measurements( + sacc_galaxy_lens0_no_two_point_data: sacc.Sacc, ) -> None: - sacc_data, _, _ = sacc_galaxy_xis_lens0_lens0_no_data + """Reject setup without correlation measurements or a theta configuration. - src0 = NumberCounts(sacc_tracer="lens0") + :param sacc_galaxy_lens0_no_two_point_data: Redshift tracer without two-point data. + """ + lens0 = NumberCounts(sacc_tracer="lens0") - statistic = tp.TwoPoint("galaxy_density_xi", src0, src0) + statistic = tp.TwoPoint("galaxy_density_xi", lens0, lens0) with pytest.warns(UserWarning, match="Empty index selected"): with pytest.raises( RuntimeError, @@ -396,13 +414,13 @@ def test_two_point_lens0_lens0_no_data_error( "and no input theta values were given" ), ): - statistic.read(sacc_data) + statistic.read(sacc_galaxy_lens0_no_two_point_data) def test_two_point_src0_src0_data_and_conf_warn( - sacc_galaxy_cells_src0_src0_window, + sacc_galaxy_cells_src0_src0_window: tuple[sacc.Sacc, TomographicBin], ) -> None: - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_window + sacc_data, _ = sacc_galaxy_cells_src0_src0_window src0 = WeakLensing(sacc_tracer="src0") @@ -424,8 +442,10 @@ def test_two_point_src0_src0_data_and_conf_warn( statistic.read(sacc_data) -def test_two_point_lens0_lens0_data_and_conf_warn(sacc_galaxy_xis_lens0_lens0) -> None: - sacc_data, _, _ = sacc_galaxy_xis_lens0_lens0 +def test_two_point_lens0_lens0_data_and_conf_warn( + sacc_galaxy_xis_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, _ = sacc_galaxy_xis_lens0_lens0 src0 = NumberCounts(sacc_tracer="lens0") @@ -644,7 +664,7 @@ class ModelWithApplyInterpWhen(BaseModel): apply_interp_when: tp.ApplyInterpolationWhen -def test_apply_interp_when_serialization_default(): +def test_apply_interp_when_serialization_default() -> None: model = ModelWithApplyInterpWhen( apply_interp_when=tp.ApplyInterpolationWhen.DEFAULT ) @@ -657,7 +677,7 @@ def test_apply_interp_when_serialization_default(): assert model.apply_interp_when == tp.ApplyInterpolationWhen.DEFAULT -def test_apply_interp_when_serialization_all(): +def test_apply_interp_when_serialization_all() -> None: model = ModelWithApplyInterpWhen(apply_interp_when=tp.ApplyInterpolationWhen.ALL) assert model.apply_interp_when == tp.ApplyInterpolationWhen.ALL @@ -668,7 +688,7 @@ def test_apply_interp_when_serialization_all(): assert model.apply_interp_when == tp.ApplyInterpolationWhen.ALL -def test_apply_interp_when_serialization_none(): +def test_apply_interp_when_serialization_none() -> None: model = ModelWithApplyInterpWhen(apply_interp_when=tp.ApplyInterpolationWhen.NONE) assert model.apply_interp_when == tp.ApplyInterpolationWhen.NONE @@ -679,7 +699,9 @@ def test_apply_interp_when_serialization_none(): assert model.apply_interp_when == tp.ApplyInterpolationWhen.NONE -def test_apply_interp_when_serialization(apply_interp_when: tp.ApplyInterpolationWhen): +def test_apply_interp_when_serialization( + apply_interp_when: tp.ApplyInterpolationWhen, +) -> None: model = ModelWithApplyInterpWhen(apply_interp_when=apply_interp_when) assert model.apply_interp_when == apply_interp_when @@ -690,7 +712,7 @@ def test_apply_interp_when_serialization(apply_interp_when: tp.ApplyInterpolatio assert model.apply_interp_when == apply_interp_when -def test_apply_interp_when_serialization_error(): +def test_apply_interp_when_serialization_error() -> None: with pytest.raises( TypeError, match="Cannot parse ApplyInterpolationWhen from value: None" ): @@ -704,12 +726,12 @@ def test_apply_interp_when_serialization_error(): def test_two_point_src0_src0_window_aiw( - sacc_galaxy_cells_src0_src0_window: tuple[object, ...], + sacc_galaxy_cells_src0_src0_window: tuple[sacc.Sacc, TomographicBin], apply_interp_when: tp.ApplyInterpolationWhen, ) -> None: """This test also makes sure that TwoPoint theory calculations are repeatable.""" - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_window + sacc_data, _ = sacc_galaxy_cells_src0_src0_window src0 = WeakLensing(sacc_tracer="src0") @@ -738,10 +760,10 @@ def test_two_point_src0_src0_window_aiw( def test_two_point_src0_src0_no_window_aiw( - sacc_galaxy_cells_src0_src0_no_window: tuple[object, ...], + sacc_galaxy_cells_src0_src0_no_window: tuple[sacc.Sacc, TomographicBin], apply_interp_when: tp.ApplyInterpolationWhen, ) -> None: - sacc_data, _, _ = sacc_galaxy_cells_src0_src0_no_window + sacc_data, _ = sacc_galaxy_cells_src0_src0_no_window src0 = WeakLensing(sacc_tracer="src0") @@ -800,7 +822,7 @@ def test_two_point_src0_src0_real_aiw( assert len(cells) != len(statistic.theory.ells_for_xi) -def test_calculate_pk_with_halo_model(): +def test_calculate_pk_with_halo_model() -> None: """Test calculate_pk function when tracers have halo model (covers line 126).""" from unittest.mock import Mock, patch @@ -863,7 +885,7 @@ def test_calculate_pk_with_halo_model(): tools.has_pk.assert_called_once_with("test_pk") -def test_two_point_normalize_window_default(): +def test_two_point_normalize_window_default() -> None: """Test that TwoPoint.normalize_window defaults to True.""" source = NumberCounts(sacc_tracer="lens_0") two_point = tp.TwoPoint( @@ -874,7 +896,7 @@ def test_two_point_normalize_window_default(): assert two_point.normalize_window is True -def test_two_point_normalize_window_false(): +def test_two_point_normalize_window_false() -> None: """Test that TwoPoint.normalize_window can be set to False.""" source = NumberCounts(sacc_tracer="lens_0") two_point = tp.TwoPoint( @@ -886,7 +908,7 @@ def test_two_point_normalize_window_false(): assert two_point.normalize_window is False -def test_two_point_normalize_window_true_explicit(): +def test_two_point_normalize_window_true_explicit() -> None: """Test that TwoPoint.normalize_window can be explicitly set to True.""" source = NumberCounts(sacc_tracer="lens_0") two_point = tp.TwoPoint( @@ -899,7 +921,7 @@ def test_two_point_normalize_window_true_explicit(): @pytest.fixture(name="sacc_with_window_for_two_point") -def fixture_sacc_with_window_for_two_point(tmp_path): +def fixture_sacc_with_window_for_two_point(tmp_path: Path) -> sacc.Sacc: """Create a SACC file with window functions for TwoPoint testing.""" import pyccl @@ -952,7 +974,9 @@ def fixture_sacc_with_window_for_two_point(tmp_path): return sacc_data -def test_two_point_normalize_window_used_in_read(sacc_with_window_for_two_point): +def test_two_point_normalize_window_used_in_read( + sacc_with_window_for_two_point: sacc.Sacc, +) -> None: """Test that normalize_window is actually used during read.""" # Use SACC data with window functions from fixture sacc_data = sacc_with_window_for_two_point diff --git a/tests/likelihood/gauss_family/test_const_gaussian.py b/tests/likelihood/gauss_family/test_const_gaussian.py index b072a9d81..927d7fdb7 100644 --- a/tests/likelihood/gauss_family/test_const_gaussian.py +++ b/tests/likelihood/gauss_family/test_const_gaussian.py @@ -14,7 +14,7 @@ from firecrown.likelihood._gaussfamily import Statistic from firecrown.likelihood._gaussian import ConstGaussian from firecrown.likelihood._statistic import TrivialStatistic -from firecrown.likelihood._two_point import TwoPoint +from firecrown.likelihood._two_point import TwoPoint, TwoPointFactory from firecrown.metadata_functions import TwoPointHarmonicIndex from firecrown.metadata_types import Galaxies, TracerNames from firecrown.modeling_tools import ModelingTools @@ -23,6 +23,7 @@ RequiredParameters, SamplerParameter, ) +from tests._types import SaccGalaxyWindowedFixture class StatisticWithoutIndices(TrivialStatistic): @@ -36,18 +37,20 @@ def read(self, sacc_data: sacc.Sacc) -> None: self.sacc_indices = None -def test_require_nonempty_statistics(): +def test_require_nonempty_statistics() -> None: with pytest.raises(ValueError): _ = ConstGaussian(statistics=[]) -def test_get_cov_fails_before_read(trivial_stats): +def test_get_cov_fails_before_read(trivial_stats: list[TrivialStatistic]) -> None: likelihood = ConstGaussian(statistics=trivial_stats) with pytest.raises(AssertionError): _ = likelihood.get_cov() -def test_read_with_wrong_statistic_fails(sacc_data_for_trivial_stat): +def test_read_with_wrong_statistic_fails( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: # Make the first statistic defective. defective_stat = StatisticWithoutIndices() likelihood = ConstGaussian(statistics=[defective_stat]) @@ -58,20 +61,26 @@ def test_read_with_wrong_statistic_fails(sacc_data_for_trivial_stat): likelihood.read(sacc_data_for_trivial_stat) -def test_get_cov_works_after_read(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_works_after_read( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_cov() == np.diag([4.0, 9.0, 16.0])) -def test_get_cov_with_one_statistic(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_with_one_statistic( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_cov(trivial_stats[0]) == np.diag([4.0, 9.0, 16.0])) -def test_get_cov_with_list_of_statistics(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_with_list_of_statistics( + trivial_stats: list[Statistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -80,7 +89,9 @@ def test_get_cov_with_list_of_statistics(trivial_stats, sacc_data_for_trivial_st assert np.all(cov == np.diag([4.0, 9.0, 16.0])) -def test_get_data_vector_fails_before_read(trivial_stats): +def test_get_data_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -89,13 +100,17 @@ def test_get_data_vector_fails_before_read(trivial_stats): _ = likelihood.get_data_vector() -def test_get_data_vector_works_after_read(trivial_stats, sacc_data_for_trivial_stat): +def test_get_data_vector_works_after_read( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_data_vector() == np.array([1.0, 4.0, -3.0])) -def test_compute_theory_vector_fails_before_read(trivial_stats): +def test_compute_theory_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -105,8 +120,8 @@ def test_compute_theory_vector_fails_before_read(trivial_stats): def test_compute_theory_vector_fails_before_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) with pytest.raises( @@ -117,8 +132,8 @@ def test_compute_theory_vector_fails_before_update( def test_compute_theory_vector_works_after_read_and_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -128,7 +143,9 @@ def test_compute_theory_vector_works_after_read_and_update( ) -def test_compute_theory_vector_called_twice(trivial_stats, sacc_data_for_trivial_stat): +def test_compute_theory_vector_called_twice( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -137,7 +154,9 @@ def test_compute_theory_vector_called_twice(trivial_stats, sacc_data_for_trivial assert np.all(res_1 == res_2) -def test_get_theory_vector_fails_before_read(trivial_stats): +def test_get_theory_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -149,8 +168,8 @@ def test_get_theory_vector_fails_before_read(trivial_stats): def test_get_theory_vector_fails_before_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) with pytest.raises( @@ -163,8 +182,8 @@ def test_get_theory_vector_fails_before_update( def test_get_theory_vector_fails_before_compute_theory_vector( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -178,8 +197,8 @@ def test_get_theory_vector_fails_before_compute_theory_vector( def test_get_theory_vector_works_after_read_update_and_compute_theory_vector( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -188,8 +207,8 @@ def test_get_theory_vector_works_after_read_update_and_compute_theory_vector( def test_get_theory_vector_fails_after_read_update_compute_theory_vector_and_reset( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -204,7 +223,9 @@ def test_get_theory_vector_fails_after_read_update_compute_theory_vector_and_res _ = likelihood.get_theory_vector() -def test_compute_chisq_fails_before_read(trivial_stats): +def test_compute_chisq_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: """Note that the error message from the direct call to compute_chisq notes that update() must be called; this can only be called after read().""" likelihood = ConstGaussian(statistics=trivial_stats) @@ -215,14 +236,22 @@ def test_compute_chisq_fails_before_read(trivial_stats): _ = likelihood.compute_chisq(ModelingTools()) -def test_compute_chisq(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_compute_chisq( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) assert likelihood.compute_chisq(ModelingTools()) == 2.0 -def test_deprecated_compute(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_deprecated_compute( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -233,8 +262,10 @@ def test_deprecated_compute(trivial_stats, sacc_data_for_trivial_stat, trivial_p def test_chisquared_compute_vector_not_implemented( - trivial_stats, sacc_data_for_trivial_stat, trivial_params -): + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -253,7 +284,11 @@ def compute( assert likelihood.compute_chisq(ModelingTools()) == 2.0 -def test_required_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_required_parameters( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -263,7 +298,11 @@ def test_required_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_ assert likelihood.required_parameters() == expected_params -def test_derived_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_derived_parameters( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -271,7 +310,11 @@ def test_derived_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_p assert likelihood.get_derived_parameters() == expected_params -def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_reset( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -282,7 +325,9 @@ def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params): assert not trivial_stats[0].computed_theory_vector -def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): +def test_missing_covariance( + trivial_stats: list[TrivialStatistic], sacc_with_data_points: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) with pytest.raises( RuntimeError, @@ -293,10 +338,10 @@ def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): def test_using_good_sacc( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -307,8 +352,8 @@ def test_using_good_sacc( def test_after_read_all_statistics_are_ready( - trivial_stats, sacc_data_for_trivial_stat: sacc.Sacc -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) for gs in likelihood.statistics: @@ -317,10 +362,10 @@ def test_after_read_all_statistics_are_ready( def test_make_realization_chisq( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -344,10 +389,10 @@ def test_make_realization_chisq( def test_make_realization_chisq_mean( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -373,10 +418,10 @@ def test_make_realization_chisq_mean( def test_make_realization_data_vector( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -416,10 +461,10 @@ def test_make_realization_data_vector( def test_make_realization_no_noise( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -437,9 +482,9 @@ def test_make_realization_no_noise( def test_get_sacc_indices( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -454,9 +499,9 @@ def test_get_sacc_indices( def test_get_sacc_indices_single_stat( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, -): +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -466,14 +511,17 @@ def test_get_sacc_indices_single_stat( def test_access_required_parameters( - trivial_stats, -): + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussian(statistics=trivial_stats) params = likelihood.required_parameters().get_default_values() assert params == {"mean": 0.0} -def test_create_ready(sacc_galaxy_cwindows, tp_factory): +def test_create_ready( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -485,7 +533,10 @@ def test_create_ready(sacc_galaxy_cwindows, tp_factory): assert isinstance(likelihood, ConstGaussian) -def test_create_ready_wrong_size(sacc_galaxy_cwindows, tp_factory): +def test_create_ready_wrong_size( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -502,7 +553,7 @@ def test_create_ready_wrong_size(sacc_galaxy_cwindows, tp_factory): ConstGaussian.create_ready(two_points, np.diag([1.0, 2.0, 3.0])) -def test_create_ready_not_ready(tp_factory): +def test_create_ready_not_ready(tp_factory: TwoPointFactory) -> None: metadata: TwoPointHarmonicIndex = { "data_type": "galaxy_density_xi", "tracer_names": TracerNames("lens0", "lens0"), @@ -519,8 +570,10 @@ def test_create_ready_not_ready(tp_factory): def test_compute_chisq_without_cholesky( - trivial_stats, sacc_data_for_trivial_stat, trivial_params -): + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: """Test compute_chisq with use_cholesky=False. This test ensures that the parent GaussFamily.compute_chisq_impl() method diff --git a/tests/likelihood/gauss_family/test_const_gaussianPM.py b/tests/likelihood/gauss_family/test_const_gaussianPM.py index d057696a6..39555533e 100644 --- a/tests/likelihood/gauss_family/test_const_gaussianPM.py +++ b/tests/likelihood/gauss_family/test_const_gaussianPM.py @@ -2,7 +2,8 @@ import os import re -from collections.abc import Sequence +from collections.abc import Callable, Sequence +from typing import cast import numpy as np import numpy.typing as npt @@ -16,9 +17,20 @@ import firecrown.likelihood.weak_lensing as wl import firecrown.updatable from firecrown.data_functions import extract_all_harmonic_data -from firecrown.likelihood import ConstGaussianPM, Statistic, TrivialStatistic, TwoPoint +from firecrown.likelihood import ( + ConstGaussianPM, + Statistic, + TrivialStatistic, + TwoPoint, + TwoPointFactory, +) from firecrown.likelihood._gaussian_pointmass import PointMassData -from firecrown.likelihood_base import SourceGalaxy, SourceGalaxyArgs, Tracer +from firecrown.likelihood_base import ( + GuardedStatistic, + SourceGalaxy, + SourceGalaxyArgs, + Tracer, +) from firecrown.metadata_functions import TwoPointHarmonicIndex from firecrown.metadata_types import Galaxies, TracerNames from firecrown.modeling_tools import ModelingTools @@ -27,6 +39,7 @@ RequiredParameters, SamplerParameter, ) +from tests._types import SaccGalaxyWindowedFixture # Tests that are inherited from ConstGaussian: @@ -42,18 +55,20 @@ def read(self, sacc_data: sacc.Sacc) -> None: self.sacc_indices = None -def test_require_nonempty_statistics(): +def test_require_nonempty_statistics() -> None: with pytest.raises(ValueError): _ = ConstGaussianPM(statistics=[]) -def test_get_cov_fails_before_read(trivial_stats): +def test_get_cov_fails_before_read(trivial_stats: list[TrivialStatistic]) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) with pytest.raises(AssertionError): _ = likelihood.get_cov() -def test_read_with_wrong_statistic_fails(sacc_data_for_trivial_stat): +def test_read_with_wrong_statistic_fails( + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: # Make the first statistic defective. defective_stat = StatisticWithoutIndices() likelihood = ConstGaussianPM(statistics=[defective_stat]) @@ -64,20 +79,26 @@ def test_read_with_wrong_statistic_fails(sacc_data_for_trivial_stat): likelihood.read(sacc_data_for_trivial_stat) -def test_get_cov_works_after_read(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_works_after_read( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_cov() == np.diag([4.0, 9.0, 16.0])) -def test_get_cov_with_one_statistic(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_with_one_statistic( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_cov(trivial_stats[0]) == np.diag([4.0, 9.0, 16.0])) -def test_get_cov_with_list_of_statistics(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_with_list_of_statistics( + trivial_stats: list[Statistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -86,7 +107,9 @@ def test_get_cov_with_list_of_statistics(trivial_stats, sacc_data_for_trivial_st assert np.all(cov == np.diag([4.0, 9.0, 16.0])) -def test_get_data_vector_fails_before_read(trivial_stats): +def test_get_data_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -95,13 +118,17 @@ def test_get_data_vector_fails_before_read(trivial_stats): _ = likelihood.get_data_vector() -def test_get_data_vector_works_after_read(trivial_stats, sacc_data_for_trivial_stat): +def test_get_data_vector_works_after_read( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_data_vector() == np.array([1.0, 4.0, -3.0])) -def test_compute_theory_vector_fails_before_read(trivial_stats): +def test_compute_theory_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -111,8 +138,8 @@ def test_compute_theory_vector_fails_before_read(trivial_stats): def test_compute_theory_vector_fails_before_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) with pytest.raises( @@ -123,8 +150,8 @@ def test_compute_theory_vector_fails_before_update( def test_compute_theory_vector_works_after_read_and_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -134,7 +161,9 @@ def test_compute_theory_vector_works_after_read_and_update( ) -def test_compute_theory_vector_called_twice(trivial_stats, sacc_data_for_trivial_stat): +def test_compute_theory_vector_called_twice( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -143,7 +172,9 @@ def test_compute_theory_vector_called_twice(trivial_stats, sacc_data_for_trivial assert np.all(res_1 == res_2) -def test_get_theory_vector_fails_before_read(trivial_stats): +def test_get_theory_vector_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) with pytest.raises( AssertionError, @@ -155,8 +186,8 @@ def test_get_theory_vector_fails_before_read(trivial_stats): def test_get_theory_vector_fails_before_update( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) with pytest.raises( @@ -169,8 +200,8 @@ def test_get_theory_vector_fails_before_update( def test_get_theory_vector_fails_before_compute_theory_vector( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -184,8 +215,8 @@ def test_get_theory_vector_fails_before_compute_theory_vector( def test_get_theory_vector_works_after_read_update_and_compute_theory_vector( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -194,8 +225,8 @@ def test_get_theory_vector_works_after_read_update_and_compute_theory_vector( def test_get_theory_vector_fails_after_read_update_compute_theory_vector_and_reset( - trivial_stats, sacc_data_for_trivial_stat -): + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(firecrown.updatable.ParamsMap(mean=10.5)) @@ -210,7 +241,9 @@ def test_get_theory_vector_fails_after_read_update_compute_theory_vector_and_res _ = likelihood.get_theory_vector() -def test_compute_chisq_fails_before_read(trivial_stats): +def test_compute_chisq_fails_before_read( + trivial_stats: list[TrivialStatistic], +) -> None: """Note that the error message from the direct call to compute_chisq notes that update() must be called; this can only be called after read().""" likelihood = ConstGaussianPM(statistics=trivial_stats) @@ -223,7 +256,11 @@ def test_compute_chisq_fails_before_read(trivial_stats): _ = likelihood.compute_chisq(ModelingTools()) -def test_compute_chisq(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_compute_chisq( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -234,7 +271,11 @@ def test_compute_chisq(trivial_stats, sacc_data_for_trivial_stat, trivial_params assert likelihood.compute_chisq(ModelingTools()) == 2.0 -def test_deprecated_compute(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_deprecated_compute( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -245,8 +286,10 @@ def test_deprecated_compute(trivial_stats, sacc_data_for_trivial_stat, trivial_p def test_chisquared_compute_vector_not_implemented( - trivial_stats, sacc_data_for_trivial_stat, trivial_params -): + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -269,7 +312,11 @@ def compute( assert likelihood.compute_chisq(ModelingTools()) == 2.0 -def test_required_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_required_parameters( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -279,7 +326,11 @@ def test_required_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_ assert likelihood.required_parameters() == expected_params -def test_derived_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_derived_parameters( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -287,7 +338,11 @@ def test_derived_parameters(trivial_stats, sacc_data_for_trivial_stat, trivial_p assert likelihood.get_derived_parameters() == expected_params -def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params): +def test_reset( + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params: firecrown.updatable.ParamsMap, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params) @@ -302,7 +357,9 @@ def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params): assert not trivial_stats[0].computed_theory_vector -def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): +def test_missing_covariance( + trivial_stats: list[TrivialStatistic], sacc_with_data_points: sacc.Sacc +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) with pytest.raises( RuntimeError, @@ -313,10 +370,10 @@ def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): def test_using_good_sacc( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -331,8 +388,9 @@ def test_using_good_sacc( def test_after_read_all_statistics_are_ready( - trivial_stats, sacc_data_for_trivial_stat: sacc.Sacc -): + trivial_stats: list[TrivialStatistic], + sacc_data_for_trivial_stat: sacc.Sacc, +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) for gs in likelihood.statistics: @@ -341,10 +399,10 @@ def test_after_read_all_statistics_are_ready( def test_make_realization_chisq( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -376,10 +434,10 @@ def test_make_realization_chisq( def test_make_realization_chisq_mean( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -413,10 +471,10 @@ def test_make_realization_chisq_mean( def test_make_realization_data_vector( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -460,10 +518,10 @@ def test_make_realization_data_vector( def test_make_realization_no_noise( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5) @@ -485,9 +543,9 @@ def test_make_realization_no_noise( def test_get_sacc_indices( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -502,9 +560,9 @@ def test_get_sacc_indices( def test_get_sacc_indices_single_stat( - trivial_stats, + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc, -): +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -514,14 +572,17 @@ def test_get_sacc_indices_single_stat( def test_access_required_parameters( - trivial_stats, -): + trivial_stats: list[TrivialStatistic], +) -> None: likelihood = ConstGaussianPM(statistics=trivial_stats) params = likelihood.required_parameters().get_default_values() assert params == {"mean": 0.0} -def test_create_ready(sacc_galaxy_cwindows, tp_factory): +def test_create_ready( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -533,7 +594,10 @@ def test_create_ready(sacc_galaxy_cwindows, tp_factory): assert isinstance(likelihood, ConstGaussianPM) -def test_create_ready_wrong_size(sacc_galaxy_cwindows, tp_factory): +def test_create_ready_wrong_size( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -550,7 +614,7 @@ def test_create_ready_wrong_size(sacc_galaxy_cwindows, tp_factory): ConstGaussianPM.create_ready(two_points, np.diag([1.0, 2.0, 3.0])) -def test_create_ready_not_ready(tp_factory): +def test_create_ready_not_ready(tp_factory: TwoPointFactory) -> None: metadata: TwoPointHarmonicIndex = { "data_type": "galaxy_density_xi", "tracer_names": TracerNames("lens0", "lens0"), @@ -572,7 +636,14 @@ def test_create_ready_not_ready(tp_factory): class MockStatistic: """A mock statistic with the relevant properties needed to test PM.""" - def __init__(self, sacc_data_type, source0, source1, thetas, get_data_vector): + def __init__( + self, + sacc_data_type: str, + source0: SourceGalaxy[SourceGalaxyArgs], + source1: SourceGalaxy[SourceGalaxyArgs], + thetas: list[float], + get_data_vector: Callable[[], npt.NDArray[np.float64]], + ) -> None: self.sacc_data_type = sacc_data_type self.source0 = source0 self.source1 = source1 @@ -630,7 +701,7 @@ def create_tracers(self, tools: ModelingTools) -> tuple[Sequence[Tracer], object class MockStatisticContainer: """A mock statistic container with the relevant properties needed to test PM.""" - def __init__(self, statistic): + def __init__(self, statistic: object) -> None: self.statistic = statistic @@ -677,14 +748,14 @@ def fixture_minimal_const_gaussian_PM() -> ConstGaussianPM: # pylint: disable=protected-access -def test_precomputed_warning(minimal_const_gaussian_PM): +def test_precomputed_warning(minimal_const_gaussian_PM: ConstGaussianPM) -> None: # Check that running the precomputation twice gives a warning. minimal_const_gaussian_PM._generate_maps() with pytest.warns(UserWarning): minimal_const_gaussian_PM._generate_maps() -def test_uneven_nz_size_error_lens(minimal_const_gaussian_PM): +def test_uneven_nz_size_error_lens(minimal_const_gaussian_PM: ConstGaussianPM) -> None: # Check the case of uneven lens N(z) lengths. tracer_args = SourceGalaxyArgs( z=np.array([0.1, 0.2, 0.3]), dndz=np.array([1.0, 2.0, 3.0]) @@ -702,7 +773,8 @@ def test_uneven_nz_size_error_lens(minimal_const_gaussian_PM): lambda: np.array([1.0, 2.0, 3.0]), ) stat_container = MockStatisticContainer(statistic) - minimal_const_gaussian_PM.statistics.append(stat_container) + # PM internals read only .statistic from these test doubles. + minimal_const_gaussian_PM.statistics.append(cast(GuardedStatistic, stat_container)) with pytest.raises( AssertionError, match=re.escape("N(z) sampling must be the same for all lens tracers."), @@ -710,7 +782,9 @@ def test_uneven_nz_size_error_lens(minimal_const_gaussian_PM): minimal_const_gaussian_PM._generate_maps() -def test_uneven_nz_size_error_source(minimal_const_gaussian_PM): +def test_uneven_nz_size_error_source( + minimal_const_gaussian_PM: ConstGaussianPM, +) -> None: # Check the case of uneven source N(z) lengths. tracer_args = SourceGalaxyArgs( z=np.array([0.1, 0.2, 0.3]), dndz=np.array([1.0, 2.0, 3.0]) @@ -728,7 +802,8 @@ def test_uneven_nz_size_error_source(minimal_const_gaussian_PM): lambda: np.array([1.0, 2.0, 3.0]), ) stat_container = MockStatisticContainer(statistic) - minimal_const_gaussian_PM.statistics.append(stat_container) + # PM internals read only .statistic from these test doubles. + minimal_const_gaussian_PM.statistics.append(cast(GuardedStatistic, stat_container)) with pytest.raises( AssertionError, match=re.escape("N(z) sampling must be the same for all source tracers."), @@ -736,7 +811,7 @@ def test_uneven_nz_size_error_source(minimal_const_gaussian_PM): minimal_const_gaussian_PM._generate_maps() -def test_prepare_integrand(minimal_const_gaussian_PM): +def test_prepare_integrand(minimal_const_gaussian_PM: ConstGaussianPM) -> None: # Check that _prepare_integrand() behaves as expected. cosmo = pyccl.CosmologyVanillaLCDM() @@ -760,7 +835,7 @@ def test_prepare_integrand(minimal_const_gaussian_PM): assert np.all(minimal_const_gaussian_PM._prepare_integrand(cosmo)) >= 0.0 -def test_compute_betas(minimal_const_gaussian_PM): +def test_compute_betas(minimal_const_gaussian_PM: ConstGaussianPM) -> None: # Check that _compute_betas() generate the correct number of betas. cosmo = pyccl.CosmologyVanillaLCDM() tracer_args = SourceGalaxyArgs( @@ -776,7 +851,8 @@ def test_compute_betas(minimal_const_gaussian_PM): lambda: np.array([1.0, 2.0, 3.0]), ) stat_container = MockStatisticContainer(statistic) - minimal_const_gaussian_PM.statistics.append(stat_container) + # PM internals read only .statistic from these test doubles. + minimal_const_gaussian_PM.statistics.append(cast(GuardedStatistic, stat_container)) minimal_const_gaussian_PM._generate_maps() assert np.shape(minimal_const_gaussian_PM._compute_betas(cosmo)) == (1, 2) @@ -788,7 +864,7 @@ def fixture_sacc_data() -> sacc.Sacc: return sacc.Sacc.load_fits(saccfile) -def test_PM_correction_matrix(sacc_data): +def test_PM_correction_matrix(sacc_data: sacc.Sacc) -> None: # Check that the inverse covariance matrix changes. stats = {} stats["xip_src0_src0"] = TwoPoint( @@ -818,7 +894,7 @@ def test_PM_correction_matrix(sacc_data): assert not np.isinf(likelihood.inv_cov).any() -def test_compute_chisq_with_correction(sacc_data): +def test_compute_chisq_with_correction(sacc_data: sacc.Sacc) -> None: # Test that compute_chisq_impl works correctly when inv_cov_correction is set # This tests the truthy branch of the if statement at line 50 stats = {} @@ -847,7 +923,7 @@ def test_compute_chisq_with_correction(sacc_data): assert np.isfinite(chisq) -def test_get_lens_statistic_not_found(sacc_data): +def test_get_lens_statistic_not_found(sacc_data: sacc.Sacc) -> None: # Test that _get_lens_statistic raises StopIteration when lens tracer not found stats = {} stats["gammat_lens0_src0"] = TwoPoint( @@ -863,7 +939,7 @@ def test_get_lens_statistic_not_found(sacc_data): likelihood._get_lens_statistic("nonexistent") -def test_get_src_statistic_not_found(sacc_data): +def test_get_src_statistic_not_found(sacc_data: sacc.Sacc) -> None: # Test that _get_src_statistic raises StopIteration when source tracer not found stats = {} stats["gammat_lens0_src0"] = TwoPoint( @@ -881,11 +957,15 @@ def test_get_src_statistic_not_found(sacc_data): likelihood._get_src_statistic("nonexistent") -def test_collect_data_vectors_missing_attributes(): +def test_collect_data_vectors_missing_attributes() -> None: # Test that _collect_data_vectors raises StopIteration when statistics # are missing required attributes class IncompleteStatistic: - def __init__(self): + sacc_data_type: str + source0: MockSource + source1: MockSource + + def __init__(self) -> None: # Missing 'thetas' attribute self.sacc_data_type = "galaxy_shearDensity_xi_t" tracer_args = SourceGalaxyArgs(z=np.array([0.1]), dndz=np.array([1.0])) @@ -907,7 +987,10 @@ def __init__(self): ] ) # Replace statistics with mock - use object.__setattr__ to bypass type checking - object.__setattr__(likelihood, "statistics", [stat_container]) + # The double omits `thetas` to trigger _collect_data_vectors' StopIteration. + object.__setattr__( + likelihood, "statistics", [cast(GuardedStatistic, stat_container)] + ) with pytest.raises(StopIteration, match="missing attributes"): likelihood._collect_data_vectors() diff --git a/tests/likelihood/gauss_family/test_student_t.py b/tests/likelihood/gauss_family/test_student_t.py index 159a1577d..3243e11d5 100644 --- a/tests/likelihood/gauss_family/test_student_t.py +++ b/tests/likelihood/gauss_family/test_student_t.py @@ -17,44 +17,52 @@ @pytest.fixture(name="trivial_params_student_t") -def fixture_trivial_params_student_t(trivial_params) -> ParamsMap: +def fixture_trivial_params_student_t(trivial_params: ParamsMap) -> ParamsMap: """Return a ParamsMap with one parameter.""" trivial_params["nu"] = 5.0 return trivial_params -def test_require_nonempty_statistics(): +def test_require_nonempty_statistics() -> None: with pytest.raises(ValueError): _ = StudentT(statistics=[]) -def test_make_realization_vector_not_supported(trivial_stats): +def test_make_realization_vector_not_supported( + trivial_stats: list[Statistic], +) -> None: likelihood = StudentT(statistics=trivial_stats) with pytest.raises(NotImplementedError, match="StudentT"): likelihood.make_realization_vector() -def test_update_fails_before_read(trivial_stats, trivial_params_student_t): +def test_update_fails_before_read( + trivial_stats: list[Statistic], trivial_params_student_t: ParamsMap +) -> None: likelihood = StudentT(statistics=trivial_stats) with pytest.raises(AssertionError): likelihood.update(trivial_params_student_t) -def test_get_cov_fails_before_read(trivial_stats): +def test_get_cov_fails_before_read(trivial_stats: list[Statistic]) -> None: likelihood = StudentT(statistics=trivial_stats) with pytest.raises(AssertionError): _ = likelihood.get_cov() -def test_get_cov_works_after_read(trivial_stats, sacc_data_for_trivial_stat): +def test_get_cov_works_after_read( + trivial_stats: list[Statistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) assert np.all(likelihood.get_cov() == np.diag([4.0, 9.0, 16.0])) def test_chisquared( - trivial_stats, sacc_data_for_trivial_stat, trivial_params_student_t -): + trivial_stats: list[Statistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params_student_t: ParamsMap, +) -> None: trivial_params_student_t["nu"] = 5.0 likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) @@ -63,8 +71,10 @@ def test_chisquared( def test_required_parameters( - trivial_stats, sacc_data_for_trivial_stat, trivial_params_student_t -): + trivial_stats: list[Statistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params_student_t: ParamsMap, +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params_student_t) @@ -78,8 +88,10 @@ def test_required_parameters( def test_derived_parameters( - trivial_stats, sacc_data_for_trivial_stat, trivial_params_student_t -): + trivial_stats: list[Statistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params_student_t: ParamsMap, +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params_student_t) @@ -87,7 +99,11 @@ def test_derived_parameters( assert likelihood.get_derived_parameters() == expected_params -def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params_student_t): +def test_reset( + trivial_stats: list[Statistic], + sacc_data_for_trivial_stat: sacc.Sacc, + trivial_params_student_t: ParamsMap, +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) likelihood.update(trivial_params_student_t) @@ -98,7 +114,9 @@ def test_reset(trivial_stats, sacc_data_for_trivial_stat, trivial_params_student assert not trivial_stats[0].computed_theory_vector -def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): +def test_missing_covariance( + trivial_stats: list[Statistic], sacc_with_data_points: sacc.Sacc +) -> None: likelihood = StudentT(statistics=trivial_stats) with pytest.raises( RuntimeError, @@ -109,10 +127,10 @@ def test_missing_covariance(trivial_stats, sacc_with_data_points: sacc.Sacc): def test_using_good_sacc( - trivial_stats, + trivial_stats: list[Statistic], sacc_data_for_trivial_stat: sacc.Sacc, tools_with_vanilla_cosmology: ModelingTools, -): +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) params = firecrown.updatable.ParamsMap(mean=10.5, nu=5.0) @@ -123,8 +141,8 @@ def test_using_good_sacc( def test_after_read_all_statistics_are_ready( - trivial_stats, sacc_data_for_trivial_stat: sacc.Sacc -): + trivial_stats: list[Statistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: likelihood = StudentT(statistics=trivial_stats) likelihood.read(sacc_data_for_trivial_stat) for gs in likelihood.statistics: diff --git a/tests/likelihood/lkdir/lk_derived_parameter.py b/tests/likelihood/lkdir/lk_derived_parameter.py index 5a46b64a5..665d11734 100644 --- a/tests/likelihood/lkdir/lk_derived_parameter.py +++ b/tests/likelihood/lkdir/lk_derived_parameter.py @@ -13,7 +13,9 @@ from . import lkmodule -def build_likelihood(_: NamedParameters): +def build_likelihood( + _: NamedParameters, +) -> tuple[lkmodule.DerivedParameterLikelihood, ModelingTools]: """Return a DerivedParameterLikelihood object.""" tools = ModelingTools( ccl_factory=CCLFactory(amplitude_parameter=PoweSpecAmplitudeParameter.SIGMA8) diff --git a/tests/likelihood/lkdir/lk_needing_param.py b/tests/likelihood/lkdir/lk_needing_param.py index 58af3a186..1f34deafd 100644 --- a/tests/likelihood/lkdir/lk_needing_param.py +++ b/tests/likelihood/lkdir/lk_needing_param.py @@ -13,7 +13,9 @@ from . import lkmodule -def build_likelihood(params: NamedParameters): +def build_likelihood( + params: NamedParameters, +) -> tuple[lkmodule.ParamaterizedLikelihood, ModelingTools]: """Return a ParameterizedLikelihood object.""" tools = ModelingTools( ccl_factory=CCLFactory(amplitude_parameter=PoweSpecAmplitudeParameter.SIGMA8) diff --git a/tests/likelihood/lkdir/lk_sampler_parameter.py b/tests/likelihood/lkdir/lk_sampler_parameter.py index 1a0159ba1..c0f389c8d 100644 --- a/tests/likelihood/lkdir/lk_sampler_parameter.py +++ b/tests/likelihood/lkdir/lk_sampler_parameter.py @@ -14,7 +14,9 @@ from . import lkmodule -def build_likelihood(params: NamedParameters): +def build_likelihood( + params: NamedParameters, +) -> tuple[lkmodule.SamplerParameterLikelihood, ModelingTools]: """Return a SamplerParameterLikelihood object.""" tools = ModelingTools( ccl_factory=CCLFactory(amplitude_parameter=PoweSpecAmplitudeParameter.SIGMA8) diff --git a/tests/likelihood/lkdir/lkmodule.py b/tests/likelihood/lkdir/lkmodule.py index f8ba95fab..578547db7 100644 --- a/tests/likelihood/lkdir/lkmodule.py +++ b/tests/likelihood/lkdir/lkmodule.py @@ -75,7 +75,7 @@ class DerivedParameterLikelihood(Likelihood): """A minimal likelihood for testing. This likelihood requires a parameter named 'sacc_filename'.""" - def __init__(self): + def __init__(self) -> None: """Initialize the DerivedParameterLikelihood where _get_derived_parameters creates a derived parameter called "derived_param0". """ @@ -95,16 +95,16 @@ def compute_loglike(self, tools: ModelingTools) -> float: return -3.14 -def parameterized_likelihood(params: NamedParameters): +def parameterized_likelihood(params: NamedParameters) -> ParamaterizedLikelihood: """Return a ParameterizedLikelihood object.""" return ParamaterizedLikelihood(params) -def sampler_parameter_likelihood(params: NamedParameters): +def sampler_parameter_likelihood(params: NamedParameters) -> SamplerParameterLikelihood: """Return a SamplerParameterLikelihood object.""" return SamplerParameterLikelihood(params) -def derived_parameter_likelihood(): +def derived_parameter_likelihood() -> DerivedParameterLikelihood: """Return a DerivedParameterLikelihood object.""" return DerivedParameterLikelihood() diff --git a/tests/likelihood/lkdir/lkscript.py b/tests/likelihood/lkdir/lkscript.py index 3931e63d4..8e5e42b95 100644 --- a/tests/likelihood/lkdir/lkscript.py +++ b/tests/likelihood/lkdir/lkscript.py @@ -2,6 +2,7 @@ Provides a trivial likelihood factory function for testing purposes. """ +from firecrown.likelihood._likelihood import NamedParameters from firecrown.modeling_tools import ( CCLFactory, ModelingTools, @@ -16,7 +17,9 @@ import lkmodule # type: ignore -def build_likelihood(_): +def build_likelihood( + _: NamedParameters, +) -> tuple[lkmodule.EmptyLikelihood, ModelingTools]: """Return an EmptyLikelihood object.""" tools = ModelingTools( ccl_factory=CCLFactory(amplitude_parameter=PoweSpecAmplitudeParameter.SIGMA8) diff --git a/tests/likelihood/lkdir/lkscript_invalid.py b/tests/likelihood/lkdir/lkscript_invalid.py index 3e1197f7c..5e0c5ffcf 100644 --- a/tests/likelihood/lkdir/lkscript_invalid.py +++ b/tests/likelihood/lkdir/lkscript_invalid.py @@ -5,9 +5,11 @@ a build_likelihood Callable. """ +from firecrown.likelihood._likelihood import NamedParameters + from . import lkmodule -def not_a_build_likelihood(_): +def not_a_build_likelihood(_: NamedParameters) -> lkmodule.EmptyLikelihood: """Return an EmptyLikelihood object.""" return lkmodule.empty_likelihood() diff --git a/tests/likelihood/lkdir/lkscript_pure_ccl.py b/tests/likelihood/lkdir/lkscript_pure_ccl.py index 899e2fc7f..03b21107b 100644 --- a/tests/likelihood/lkdir/lkscript_pure_ccl.py +++ b/tests/likelihood/lkdir/lkscript_pure_ccl.py @@ -2,6 +2,7 @@ Provides a trivial likelihood factory function with PURE_CCL_MODE for testing purposes. """ +from firecrown.likelihood._likelihood import NamedParameters from firecrown.modeling_tools import ( CCLCreationMode, CCLFactory, @@ -12,7 +13,9 @@ from . import lkmodule -def build_likelihood(_): +def build_likelihood( + _: NamedParameters, +) -> tuple[lkmodule.EmptyLikelihood, ModelingTools]: """Return an EmptyLikelihood object with PURE_CCL_MODE.""" tools = ModelingTools( ccl_factory=CCLFactory( diff --git a/tests/likelihood/lkdir/lkscript_returns_wrong_type.py b/tests/likelihood/lkdir/lkscript_returns_wrong_type.py index 6e13b0f6d..d133fc775 100644 --- a/tests/likelihood/lkdir/lkscript_returns_wrong_type.py +++ b/tests/likelihood/lkdir/lkscript_returns_wrong_type.py @@ -5,7 +5,9 @@ a Likelihood object. """ +from firecrown.likelihood._likelihood import NamedParameters -def build_likelihood(_): + +def build_likelihood(_: NamedParameters) -> str: """Return an EmptyLikelihood object.""" return "Not a Likelihood" diff --git a/tests/likelihood/lkdir/lkscript_returns_wrong_type_tools.py b/tests/likelihood/lkdir/lkscript_returns_wrong_type_tools.py index 81b84b4e2..35052ff87 100644 --- a/tests/likelihood/lkdir/lkscript_returns_wrong_type_tools.py +++ b/tests/likelihood/lkdir/lkscript_returns_wrong_type_tools.py @@ -5,9 +5,11 @@ a Likelihood object. """ +from firecrown.likelihood._likelihood import NamedParameters + from . import lkmodule -def build_likelihood(_): +def build_likelihood(_: NamedParameters) -> tuple[lkmodule.EmptyLikelihood, str]: """Return an EmptyLikelihood object.""" return lkmodule.empty_likelihood(), "Not a ModelingTools" diff --git a/tests/likelihood/test_cosmosis_likelihood.py b/tests/likelihood/test_cosmosis_likelihood.py index 8a5a151b5..2b9cdea02 100644 --- a/tests/likelihood/test_cosmosis_likelihood.py +++ b/tests/likelihood/test_cosmosis_likelihood.py @@ -14,13 +14,13 @@ def make_config() -> DataBlock: return db -def test_module_cleanup(config: DataBlock): +def test_module_cleanup(config: DataBlock) -> None: """The module's cleanup function just returns zero.""" m = like.setup(config) status = like.cleanup(m) assert status == 0 -def test_module_init(config: DataBlock): +def test_module_init(config: DataBlock) -> None: module = like.setup(config) assert isinstance(module, like.FirecrownLikelihood) diff --git a/tests/likelihood/test_factories.py b/tests/likelihood/test_factories.py index 95339c5cf..df4832e7d 100644 --- a/tests/likelihood/test_factories.py +++ b/tests/likelihood/test_factories.py @@ -103,7 +103,9 @@ def test_two_point_factory_dict() -> None: "correlation_space", [TwoPointCorrelationSpace.HARMONIC, TwoPointCorrelationSpace.REAL], ) -def test_two_point_factory_to_from_dict(correlation_space) -> None: +def test_two_point_factory_to_from_dict( + correlation_space: TwoPointCorrelationSpace, +) -> None: two_point_factory = TwoPointFactory( correlation_space=correlation_space, weak_lensing_factories=[ @@ -570,7 +572,9 @@ def test_build_two_point_likelihood_invalid_likelihood_config(tmp_path: Path) -> _ = build_two_point_likelihood(build_parameters) -def test_build_two_point_harmonic_with_filter(empty_factory_harmonic) -> None: +def test_build_two_point_harmonic_with_filter( + empty_factory_harmonic: TwoPointFactory, +) -> None: two_point_experiment = TwoPointExperiment( two_point_factory=empty_factory_harmonic, data_source=DataSourceSacc( @@ -594,7 +598,7 @@ def test_build_two_point_harmonic_with_filter(empty_factory_harmonic) -> None: def test_build_two_point_harmonic_with_filter_require_filter( - empty_factory_harmonic, + empty_factory_harmonic: TwoPointFactory, ) -> None: two_point_experiment = TwoPointExperiment( two_point_factory=empty_factory_harmonic, @@ -619,7 +623,9 @@ def test_build_two_point_harmonic_with_filter_require_filter( _ = two_point_experiment.make_likelihood() -def test_build_two_point_harmonic_with_filter_empty(empty_factory_harmonic) -> None: +def test_build_two_point_harmonic_with_filter_empty( + empty_factory_harmonic: TwoPointFactory, +) -> None: two_point_experiment = TwoPointExperiment( two_point_factory=empty_factory_harmonic, data_source=DataSourceSacc( @@ -747,7 +753,7 @@ def test_build_from_measurement_harmonic_cwindow( assert isinstance(two_point0, TwoPoint) -def test_two_point_factory_get_wl_factory(): +def test_two_point_factory_get_wl_factory() -> None: wl_factory1 = WeakLensingFactory(type_source=TypeSource("ts1")) wl_factory2 = WeakLensingFactory(type_source=TypeSource("ts2")) wl_factory_default = WeakLensingFactory() @@ -766,7 +772,7 @@ def test_two_point_factory_get_wl_factory(): assert wl_factory0 is wl_factory -def test_two_point_factory_get_nc_factory(): +def test_two_point_factory_get_nc_factory() -> None: nc_factory1 = NumberCountsFactory(type_source=TypeSource("ts1")) nc_factory2 = NumberCountsFactory(type_source=TypeSource("ts2")) nc_factory_default = NumberCountsFactory() @@ -785,7 +791,7 @@ def test_two_point_factory_get_nc_factory(): assert nc_factory0 is nc_factory -def test_two_point_factory_two_equal_wls(): +def test_two_point_factory_two_equal_wls() -> None: with pytest.raises( ValueError, match="Duplicate WeakLensingFactory found for type_source ts1" ): @@ -798,7 +804,7 @@ def test_two_point_factory_two_equal_wls(): ) -def test_two_point_factory_two_equal_ncs(): +def test_two_point_factory_two_equal_ncs() -> None: with pytest.raises( ValueError, match="Duplicate NumberCountsFactory found for type_source ts1" ): @@ -811,7 +817,7 @@ def test_two_point_factory_two_equal_ncs(): ) -def test_two_point_factory_get_unavailable_wl_factory(): +def test_two_point_factory_get_unavailable_wl_factory() -> None: factory = TwoPointFactory( correlation_space=TwoPointCorrelationSpace.HARMONIC, weak_lensing_factories=[ @@ -824,7 +830,7 @@ def test_two_point_factory_get_unavailable_wl_factory(): _ = factory.get_factory(Galaxies.SHEAR_E, type_source=TypeSource("ts2")) -def test_two_point_factory_get_unavailable_nc_factory(): +def test_two_point_factory_get_unavailable_nc_factory() -> None: factory = TwoPointFactory( correlation_space=TwoPointCorrelationSpace.HARMONIC, number_counts_factories=[ @@ -850,12 +856,12 @@ def test_two_point_factory_get_unavailable_nc_factory(): ("../example.txt", Path("../example.txt")), ], ) -def test_ensure_path(file, expected): +def test_ensure_path(file: str | Path, expected: Path) -> None: result = ensure_path(file) assert result == expected -def test_ensure_path_invalid(): +def test_ensure_path_invalid() -> None: with pytest.raises( AssertionError, match="Expected code to be unreachable, but got: 123" ): @@ -863,7 +869,7 @@ def test_ensure_path_invalid(): def test_build_two_point_likelihood_harmonic_filters_result_empty( - empty_factory_harmonic, + empty_factory_harmonic: TwoPointFactory, ) -> None: """Test case where filters remove all data in harmonic space.""" two_point_experiment = TwoPointExperiment( @@ -1014,7 +1020,7 @@ def test_build_two_point_likelihood_real_with_filters_fast() -> None: assert likelihood is not None -def test_two_point_factory_two_equal_cmbs(): +def test_two_point_factory_two_equal_cmbs() -> None: """Test that duplicate CMBConvergenceFactory with same type_source raises error.""" with pytest.raises( ValueError, match="Duplicate CMBConvergenceFactory found for type_source ts1" @@ -1028,7 +1034,7 @@ def test_two_point_factory_two_equal_cmbs(): ) -def test_two_point_factory_get_cmb_factory(): +def test_two_point_factory_get_cmb_factory() -> None: """Test getting CMB factories by type_source.""" cmb_factory1 = CMBConvergenceFactory(type_source=TypeSource("ts1")) cmb_factory2 = CMBConvergenceFactory(type_source=TypeSource("ts2")) @@ -1049,7 +1055,7 @@ def test_two_point_factory_get_cmb_factory(): assert cmb_factory0 is cmb_factory -def test_two_point_factory_get_unavailable_cmb_factory(): +def test_two_point_factory_get_unavailable_cmb_factory() -> None: """Test error when requesting unavailable CMB factory type_source.""" factory = TwoPointFactory( correlation_space=TwoPointCorrelationSpace.HARMONIC, @@ -1063,7 +1069,7 @@ def test_two_point_factory_get_unavailable_cmb_factory(): _ = factory.get_factory(CMB.CONVERGENCE, type_source=TypeSource("ts2")) -def test_two_point_factory_cmb_measurement_handling(): +def test_two_point_factory_cmb_measurement_handling() -> None: """Test that CMB measurements are properly handled by get_factory.""" cmb_factory = CMBConvergenceFactory() factory = TwoPointFactory( @@ -1077,7 +1083,7 @@ def test_two_point_factory_cmb_measurement_handling(): assert result_factory is cmb_factory -def test_two_point_factory_cmb_measurement_with_type_source(): +def test_two_point_factory_cmb_measurement_with_type_source() -> None: """Test CMB measurement handling with specific type_source.""" cmb_factory1 = CMBConvergenceFactory(type_source=TypeSource("ts1")) cmb_factory2 = CMBConvergenceFactory(type_source=TypeSource("ts2")) @@ -1101,7 +1107,7 @@ def test_two_point_factory_cmb_measurement_with_type_source(): assert result_factory2 is cmb_factory2 -def test_two_point_factory_all_cmb_types(): +def test_two_point_factory_all_cmb_types() -> None: """Test that all CMB measurement types are handled correctly.""" cmb_factory = CMBConvergenceFactory() factory = TwoPointFactory( @@ -1116,7 +1122,7 @@ def test_two_point_factory_all_cmb_types(): assert result_factory is cmb_factory -def test_two_point_factory_measurement_not_supported(): +def test_two_point_factory_measurement_not_supported() -> None: """Test that measurements not supported by the factory raise an error.""" factory = TwoPointFactory( correlation_space=TwoPointCorrelationSpace.HARMONIC, @@ -1129,7 +1135,7 @@ def test_two_point_factory_measurement_not_supported(): _ = factory.get_factory(Clusters.COUNTS) -def test_two_point_factory_all_factories(): +def test_two_point_factory_all_factories() -> None: """Test that all factory types are handled correctly.""" wl_factory = WeakLensingFactory(type_source=TypeSource("ts_wl")) nc_factory = NumberCountsFactory(type_source=TypeSource("ts_nc")) @@ -1154,19 +1160,19 @@ def test_two_point_factory_all_factories(): assert cmb_result is cmb_factory -def test_load_sacc_data_file_not_found(): +def test_load_sacc_data_file_not_found() -> None: """Test load_sacc_data raises FileNotFoundError for non-existent file.""" with pytest.raises(FileNotFoundError, match="SACC file not found"): load_sacc_data("nonexistent_file.fits") -def test_load_sacc_data_file_not_found_with_path(): +def test_load_sacc_data_file_not_found_with_path() -> None: """Test load_sacc_data raises FileNotFoundError with Path object.""" with pytest.raises(FileNotFoundError, match="SACC file not found"): load_sacc_data(Path("/tmp/nonexistent_file.sacc")) -def test_load_sacc_data_both_formats_fail(tmp_path): +def test_load_sacc_data_both_formats_fail(tmp_path: Path) -> None: """Test load_sacc_data raises ValueError when file is neither HDF5 nor FITS.""" # Create a file with invalid content (not HDF5, not FITS) bad_file = tmp_path / "corrupted.sacc" @@ -1182,7 +1188,7 @@ def test_load_sacc_data_both_formats_fail(tmp_path): load_sacc_data(bad_file) -def test_load_sacc_data_ambiguous_extension_hdf5_content(tmp_path): +def test_load_sacc_data_ambiguous_extension_hdf5_content(tmp_path: Path) -> None: """Test load_sacc_data loads a .sacc-suffixed file with HDF5 content.""" hdf5_file = tmp_path / "data.sacc" s = sacc.Sacc() @@ -1194,7 +1200,7 @@ def test_load_sacc_data_ambiguous_extension_hdf5_content(tmp_path): assert isinstance(sacc_data, sacc.Sacc) -def test_load_sacc_data_ambiguous_extension_fits_content(tmp_path): +def test_load_sacc_data_ambiguous_extension_fits_content(tmp_path: Path) -> None: """Test load_sacc_data loads a .sacc-suffixed file with FITS content.""" fits_file = tmp_path / "data.sacc" s = sacc.Sacc() @@ -1206,14 +1212,14 @@ def test_load_sacc_data_ambiguous_extension_fits_content(tmp_path): assert isinstance(sacc_data, sacc.Sacc) -def test_load_sacc_data_with_path_object(): +def test_load_sacc_data_with_path_object() -> None: """Test load_sacc_data works with Path object input.""" sacc_data = load_sacc_data(Path("tests/bug_398.sacc.gz")) assert sacc_data is not None assert isinstance(sacc_data, sacc.Sacc) -def test_load_sacc_data_with_string(): +def test_load_sacc_data_with_string() -> None: """Test load_sacc_data works with string input.""" sacc_data = load_sacc_data("tests/bug_398.sacc.gz") assert sacc_data is not None @@ -1221,7 +1227,7 @@ def test_load_sacc_data_with_string(): @pytest.fixture(name="sacc_with_window", scope="function") -def fixture_sacc_with_window(tmp_path: Path): +def fixture_sacc_with_window(tmp_path: Path) -> Path: """Create a SACC file with window functions for testing normalize_window.""" # Create window function data n_ell_bin = 5 @@ -1287,13 +1293,15 @@ def fixture_sacc_with_window(tmp_path: Path): return sacc_file -def test_data_source_sacc_normalize_window_default(sacc_with_window: Path): +def test_data_source_sacc_normalize_window_default(sacc_with_window: Path) -> None: """Test that DataSourceSacc.normalize_window defaults to True.""" data_source = DataSourceSacc(sacc_data_file=str(sacc_with_window)) assert data_source.normalize_window is True -def test_data_source_sacc_normalize_window_false_dict(sacc_with_window: Path): +def test_data_source_sacc_normalize_window_false_dict( + sacc_with_window: Path, +) -> None: """Test DataSourceSacc.normalize_window can be set to False via dict.""" data_source_dict = { "sacc_data_file": str(sacc_with_window), @@ -1303,7 +1311,9 @@ def test_data_source_sacc_normalize_window_false_dict(sacc_with_window: Path): assert data_source.normalize_window is False -def test_data_source_sacc_normalize_window_false_direct(sacc_with_window: Path): +def test_data_source_sacc_normalize_window_false_direct( + sacc_with_window: Path, +) -> None: """Test DataSourceSacc.normalize_window can be set to False directly.""" data_source = DataSourceSacc( sacc_data_file=str(sacc_with_window), normalize_window=False @@ -1311,7 +1321,9 @@ def test_data_source_sacc_normalize_window_false_direct(sacc_with_window: Path): assert data_source.normalize_window is False -def test_data_source_sacc_normalize_window_true_explicit(sacc_with_window: Path): +def test_data_source_sacc_normalize_window_true_explicit( + sacc_with_window: Path, +) -> None: """Test DataSourceSacc.normalize_window can be explicitly set to True.""" data_source = DataSourceSacc( sacc_data_file=str(sacc_with_window), normalize_window=True @@ -1322,7 +1334,7 @@ def test_data_source_sacc_normalize_window_true_explicit(sacc_with_window: Path) def test_two_point_experiment_normalize_window_flows_through( empty_factory_harmonic: TwoPointFactory, sacc_with_window: Path, -): +) -> None: """Test that normalize_window flows through TwoPointExperiment to likelihood.""" # Create experiment with normalize_window=False experiment = TwoPointExperiment( @@ -1343,7 +1355,7 @@ def test_two_point_experiment_normalize_window_flows_through( def test_two_point_experiment_normalize_window_default_flows_through( empty_factory_harmonic: TwoPointFactory, sacc_with_window: Path, -): +) -> None: """Test that default normalize_window=True flows through correctly.""" experiment = TwoPointExperiment( two_point_factory=empty_factory_harmonic, @@ -1358,7 +1370,7 @@ def test_two_point_experiment_normalize_window_default_flows_through( assert likelihood is not None -def test_normalize_window_affects_extraction(sacc_with_window: Path): +def test_normalize_window_affects_extraction(sacc_with_window: Path) -> None: """Test that normalize_window parameter actually affects window extraction.""" sacc_data = load_sacc_data(sacc_with_window) @@ -1404,7 +1416,7 @@ def test_normalize_window_affects_extraction(sacc_with_window: Path): def test_two_point_experiment_yaml_with_normalize_window( tmp_path: Path, sacc_with_window: Path -): +) -> None: """Test TwoPointExperiment loading from YAML with normalize_window=False.""" tmp_experiment_file = tmp_path / "experiment_no_norm.yaml" # Use relative path from tmp_path to sacc_with_window diff --git a/tests/likelihood/test_gauss_family.py b/tests/likelihood/test_gauss_family.py index aa6d39192..0453d4718 100644 --- a/tests/likelihood/test_gauss_family.py +++ b/tests/likelihood/test_gauss_family.py @@ -7,7 +7,7 @@ import firecrown.likelihood._gaussian as g -def test_init_rejects_non_statistics(): +def test_init_rejects_non_statistics() -> None: with pytest.raises( ValueError, match=re.escape( diff --git a/tests/likelihood/test_likelihood.py b/tests/likelihood/test_likelihood.py index 825d320cf..b5cfff748 100644 --- a/tests/likelihood/test_likelihood.py +++ b/tests/likelihood/test_likelihood.py @@ -33,7 +33,7 @@ def compute_loglike(self, _: like.ModelingTools) -> float: return -1.0 -def test_integration_error_generates_0_likelihood(): +def test_integration_error_generates_0_likelihood() -> None: likelihood = LikelihoodThatThrowsIntegrationError() with pytest.warns( UserWarning, @@ -42,13 +42,13 @@ def test_integration_error_generates_0_likelihood(): assert likelihood.compute_loglike_for_sampling(like.ModelingTools()) == -np.inf -def test_other_error_propagates(): +def test_other_error_propagates() -> None: likelihood = LikelihoodThatThrowsUnhandledError() with pytest.raises(RuntimeError): likelihood.compute_loglike_for_sampling(like.ModelingTools()) -def test_factory_returns_updated_likelihood(): +def test_factory_returns_updated_likelihood() -> None: """Test warning when factory returns an already-updated likelihood.""" class PreUpdatedLikelihood(like.Likelihood): @@ -60,7 +60,7 @@ def read(self, sacc_data: sacc.Sacc) -> None: def compute_loglike(self, tools: ModelingTools) -> float: return 0.0 - def build_likelihood(_build_parameters: NamedParameters): + def build_likelihood(_build_parameters: NamedParameters) -> PreUpdatedLikelihood: """Factory that returns an updated likelihood.""" likelihood = PreUpdatedLikelihood() # Pre-update the likelihood before returning it @@ -80,7 +80,7 @@ def build_likelihood(_build_parameters: NamedParameters): assert not likelihood.is_updated() -def test_factory_returns_updated_tools(): +def test_factory_returns_updated_tools() -> None: """Test warning when factory returns already-updated tools.""" class TestLikelihood(like.Likelihood): @@ -92,7 +92,9 @@ def read(self, sacc_data: sacc.Sacc) -> None: def compute_loglike(self, tools: ModelingTools) -> float: return 0.0 - def build_likelihood(_build_parameters: NamedParameters): + def build_likelihood( + _build_parameters: NamedParameters, + ) -> tuple[TestLikelihood, ModelingTools]: """Factory that returns an updated tools object.""" likelihood = TestLikelihood() tools = ModelingTools() diff --git a/tests/likelihood/test_load_likelihood.py b/tests/likelihood/test_load_likelihood.py index cf176ca25..904643f87 100644 --- a/tests/likelihood/test_load_likelihood.py +++ b/tests/likelihood/test_load_likelihood.py @@ -15,7 +15,7 @@ ) -def test_load_likelihood_from_script_submodule(): +def test_load_likelihood_from_script_submodule() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) load_likelihood_from_script( @@ -23,7 +23,7 @@ def test_load_likelihood_from_script_submodule(): ) -def test_load_likelihood_from_script_submodule_invalid(): +def test_load_likelihood_from_script_submodule_invalid() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.raises(ValueError, match="Unrecognized Firecrown initialization file"): @@ -32,7 +32,7 @@ def test_load_likelihood_from_script_submodule_invalid(): ) -def test_load_likelihood_from_script_submodule_no_build_likelihood(): +def test_load_likelihood_from_script_submodule_no_build_likelihood() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.raises( @@ -43,7 +43,7 @@ def test_load_likelihood_from_script_submodule_no_build_likelihood(): ) -def test_load_likelihood_from_script_submodule_not_a_function(): +def test_load_likelihood_from_script_submodule_not_a_function() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.raises( @@ -55,7 +55,7 @@ def test_load_likelihood_from_script_submodule_not_a_function(): ) -def test_load_likelihood_from_script_submodule_returns_wrong_type(): +def test_load_likelihood_from_script_submodule_returns_wrong_type() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.raises( @@ -68,7 +68,7 @@ def test_load_likelihood_from_script_submodule_returns_wrong_type(): ) -def test_load_likelihood_from_script_submodule_returns_wrong_type_tools(): +def test_load_likelihood_from_script_submodule_returns_wrong_type_tools() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.raises( @@ -81,7 +81,7 @@ def test_load_likelihood_from_script_submodule_returns_wrong_type_tools(): ) -def test_load_likelihood_from_script_submodule_old(): +def test_load_likelihood_from_script_submodule_old() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) with pytest.deprecated_call(): @@ -91,7 +91,7 @@ def test_load_likelihood_from_script_submodule_old(): ) -def test_load_likelihood_from_script_correct_tools(): +def test_load_likelihood_from_script_correct_tools() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) _, tools = load_likelihood_from_script( @@ -101,7 +101,7 @@ def test_load_likelihood_from_script_correct_tools(): assert tools.test_attribute == "test" # type: ignore -def test_load_likelihood(): +def test_load_likelihood() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) _, tools = load_likelihood( @@ -111,7 +111,7 @@ def test_load_likelihood(): assert tools.test_attribute == "test" # type: ignore -def test_load_likelihood_from_module(): +def test_load_likelihood_from_module() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) module_path = os.path.join(dir_path, "lkdir") @@ -122,7 +122,7 @@ def test_load_likelihood_from_module(): assert tools.test_attribute == "test" # type: ignore -def test_load_likelihood_from_module_not_in_path(): +def test_load_likelihood_from_module_not_in_path() -> None: with pytest.raises( ValueError, match="Unrecognized Firecrown initialization module 'lkscript_not_in_path'. " @@ -132,7 +132,7 @@ def test_load_likelihood_from_module_not_in_path(): _ = load_likelihood_from_module("lkscript_not_in_path", NamedParameters()) -def test_load_likelihood_not_in_path(): +def test_load_likelihood_not_in_path() -> None: with pytest.raises( ValueError, match="Unrecognized Firecrown initialization file or module " @@ -141,7 +141,7 @@ def test_load_likelihood_not_in_path(): _ = load_likelihood("lkscript_not_in_path", NamedParameters()) -def test_load_likelihood_from_module_function_missing_likelihood_config(): +def test_load_likelihood_from_module_function_missing_likelihood_config() -> None: dir_path = os.path.dirname(os.path.realpath(__file__)) module_path = os.path.join(dir_path, "lkdir") diff --git a/tests/likelihood/test_named_parameters.py b/tests/likelihood/test_named_parameters.py index 9c0842895..53797f842 100644 --- a/tests/likelihood/test_named_parameters.py +++ b/tests/likelihood/test_named_parameters.py @@ -6,7 +6,7 @@ from firecrown.likelihood._likelihood import NamedParameters -def test_named_parameters_sanity(): +def test_named_parameters_sanity() -> None: params = NamedParameters({"a": True}) assert params.get_bool("a") is True @@ -29,7 +29,7 @@ def test_named_parameters_sanity(): assert params.to_set() == {"a"} -def test_named_parameters_default(): +def test_named_parameters_default() -> None: params = NamedParameters({}) assert params.get_bool("a", True) is True @@ -45,7 +45,7 @@ def test_named_parameters_default(): assert params.get_float("d", 1.0) == 2.2 -def test_named_parameters_wrong_type_bool(): +def test_named_parameters_wrong_type_bool() -> None: params = NamedParameters({"a": True}) with pytest.raises(AssertionError): params.get_string("a") @@ -64,7 +64,7 @@ def test_named_parameters_wrong_type_bool(): params.get_float_array("a") -def test_named_parameters_wrong_type_string(): +def test_named_parameters_wrong_type_string() -> None: params = NamedParameters({"a": "Im a string"}) with pytest.raises(AssertionError): params.get_bool("a") @@ -82,7 +82,7 @@ def test_named_parameters_wrong_type_string(): params.get_float_array("a") -def test_named_parameters_wrong_type_int(): +def test_named_parameters_wrong_type_int() -> None: params = NamedParameters({"a": 1}) with pytest.raises(AssertionError): params.get_bool("a") @@ -100,7 +100,7 @@ def test_named_parameters_wrong_type_int(): params.get_float_array("a") -def test_named_parameters_wrong_type_float(): +def test_named_parameters_wrong_type_float() -> None: params = NamedParameters({"a": 1.0}) with pytest.raises(AssertionError): params.get_bool("a") @@ -118,7 +118,7 @@ def test_named_parameters_wrong_type_float(): params.get_float_array("a") -def test_named_parameters_wrong_type_int_array(): +def test_named_parameters_wrong_type_int_array() -> None: params = NamedParameters({"a": np.array([1, 2, 3])}) with pytest.raises(AssertionError): params.get_bool("a") @@ -137,7 +137,7 @@ def test_named_parameters_wrong_type_int_array(): # params.get_float_array("a") -def test_named_parameters_wrong_type_float_array(): +def test_named_parameters_wrong_type_float_array() -> None: params = NamedParameters({"a": np.array([1.0, 2.0, 3.0])}) with pytest.raises(AssertionError): params.get_bool("a") @@ -156,7 +156,7 @@ def test_named_parameters_wrong_type_float_array(): # params.get_int_array("a") -def test_named_parameters_set_from_basic_dict_string(): +def test_named_parameters_set_from_basic_dict_string() -> None: params = NamedParameters() params.set_from_basic_dict({"a": "Im a string"}) assert params.get_string("a") == "Im a string" @@ -166,7 +166,7 @@ def test_named_parameters_set_from_basic_dict_string(): assert params.get_string("b") == "Im another string" -def test_named_parameters_set_from_basic_dict_int(): +def test_named_parameters_set_from_basic_dict_int() -> None: params = NamedParameters() params.set_from_basic_dict({"a": 1}) assert params.get_int("a") == 1 @@ -176,7 +176,7 @@ def test_named_parameters_set_from_basic_dict_int(): assert params.get_int("b") == 2 -def test_named_parameters_set_from_basic_dict_float(): +def test_named_parameters_set_from_basic_dict_float() -> None: params = NamedParameters() params.set_from_basic_dict({"a": 1.0}) assert params.get_float("a") == 1.0 @@ -186,7 +186,7 @@ def test_named_parameters_set_from_basic_dict_float(): assert params.get_float("b") == 2.0 -def test_named_parameters_set_from_basic_dict_bool(): +def test_named_parameters_set_from_basic_dict_bool() -> None: params = NamedParameters() params.set_from_basic_dict({"a": True}) assert params.get_bool("a") is True @@ -196,7 +196,7 @@ def test_named_parameters_set_from_basic_dict_bool(): assert params.get_bool("b") is False -def test_named_parameters_set_from_basic_dict_int_array(): +def test_named_parameters_set_from_basic_dict_int_array() -> None: params = NamedParameters() params.set_from_basic_dict({"a": [1, 2, 3]}) assert params.get_int_array("a").tolist() == [1, 2, 3] @@ -206,7 +206,7 @@ def test_named_parameters_set_from_basic_dict_int_array(): assert params.get_int_array("b").tolist() == [4, 5, 6] -def test_named_parameters_set_from_basic_dict_float_array(): +def test_named_parameters_set_from_basic_dict_float_array() -> None: params = NamedParameters() params.set_from_basic_dict({"a": [1.0, 2.0, 3.0]}) assert params.get_float_array("a").tolist() == [1.0, 2.0, 3.0] @@ -216,7 +216,7 @@ def test_named_parameters_set_from_basic_dict_float_array(): assert params.get_float_array("b").tolist() == [4.0, 5.0, 6.0] -def test_named_parameters_set_from_basic_dict_bool_array(): +def test_named_parameters_set_from_basic_dict_bool_array() -> None: params = NamedParameters() params.set_from_basic_dict({"a": [True, False, True]}) assert params.get_int_array("a").tolist() == [True, False, True] @@ -226,7 +226,7 @@ def test_named_parameters_set_from_basic_dict_bool_array(): assert params.get_int_array("b").tolist() == [False, True, False] -def test_named_parameters_set_from_basic_dict_mixed(): +def test_named_parameters_set_from_basic_dict_mixed() -> None: params = NamedParameters() params.set_from_basic_dict( { @@ -246,7 +246,7 @@ def test_named_parameters_set_from_basic_dict_mixed(): assert params.get_bool("f") is True -def test_named_parameters_convert_to_basic_dict_string(): +def test_named_parameters_convert_to_basic_dict_string() -> None: params = NamedParameters({"a": "Im a string"}) assert params.convert_to_basic_dict() == {"a": "Im a string"} @@ -257,7 +257,7 @@ def test_named_parameters_convert_to_basic_dict_string(): } -def test_named_parameters_convert_to_basic_dict_int(): +def test_named_parameters_convert_to_basic_dict_int() -> None: params = NamedParameters({"a": 1}) assert params.convert_to_basic_dict() == {"a": 1} @@ -265,7 +265,7 @@ def test_named_parameters_convert_to_basic_dict_int(): assert params.convert_to_basic_dict() == {"a": 1, "b": 2} -def test_named_parameters_convert_to_basic_dict_float(): +def test_named_parameters_convert_to_basic_dict_float() -> None: params = NamedParameters({"a": 1.0}) assert params.convert_to_basic_dict() == {"a": 1.0} @@ -273,7 +273,7 @@ def test_named_parameters_convert_to_basic_dict_float(): assert params.convert_to_basic_dict() == {"a": 1.0, "b": 2.0} -def test_named_parameters_convert_to_basic_dict_bool(): +def test_named_parameters_convert_to_basic_dict_bool() -> None: params = NamedParameters({"a": True}) assert params.convert_to_basic_dict() == {"a": True} @@ -281,7 +281,7 @@ def test_named_parameters_convert_to_basic_dict_bool(): assert params.convert_to_basic_dict() == {"a": True, "b": False} -def test_named_parameters_convert_to_basic_dict_int_array(): +def test_named_parameters_convert_to_basic_dict_int_array() -> None: params = NamedParameters({"a": np.array([1, 2, 3])}) assert params.convert_to_basic_dict() == {"a": [1, 2, 3]} @@ -292,7 +292,7 @@ def test_named_parameters_convert_to_basic_dict_int_array(): } -def test_named_parameters_convert_to_basic_dict_float_array(): +def test_named_parameters_convert_to_basic_dict_float_array() -> None: params = NamedParameters({"a": np.array([1.0, 2.0, 3.0])}) assert params.convert_to_basic_dict() == {"a": [1.0, 2.0, 3.0]} @@ -305,7 +305,7 @@ def test_named_parameters_convert_to_basic_dict_float_array(): } -def test_named_parameters_convert_to_basic_dict_mixed(): +def test_named_parameters_convert_to_basic_dict_mixed() -> None: params = NamedParameters( { "a": np.array([1, 2, 3]), @@ -326,7 +326,7 @@ def test_named_parameters_convert_to_basic_dict_mixed(): } -def test_named_parameters_to_set(): +def test_named_parameters_to_set() -> None: params = NamedParameters({"a": True}) assert params.to_set() == {"a"} @@ -337,38 +337,38 @@ def test_named_parameters_to_set(): assert params.to_set() == {"a", "b", "c"} -def test_named_parameters_to_float_dict(): +def test_named_parameters_to_float_dict() -> None: params = NamedParameters({"a": 1.0, "b": 2.0}) assert params.to_float_dict() == {"a": 1.0, "b": 2.0} -def test_named_parameters_to_float_dict_wrong_type(): +def test_named_parameters_to_float_dict_wrong_type() -> None: params = NamedParameters({"a": 1}) with pytest.raises(TypeError, match="only float values"): params.to_float_dict() -def test_invalid_set_from_basic_dict(): +def test_invalid_set_from_basic_dict() -> None: params = NamedParameters() with pytest.raises(ValueError): params.set_from_basic_dict({"a": {"b": "c"}}) # type: ignore -def test_invalid_set_from_basic_dict_sequence(): +def test_invalid_set_from_basic_dict_sequence() -> None: params = NamedParameters() with pytest.raises(ValueError): params.set_from_basic_dict({"a": ["a", "b", "c"]}) # type: ignore -def test_invalid_convert_to_basic_dict(): +def test_invalid_convert_to_basic_dict() -> None: params = NamedParameters({"a": {"b": "c"}}) # type: ignore with pytest.raises(ValueError): params.convert_to_basic_dict() -def test_invalid_convert_to_basic_dict_sequence(): +def test_invalid_convert_to_basic_dict_sequence() -> None: params = NamedParameters({"a": np.array(["a", "b", "c"])}) with pytest.raises(ValueError): params.convert_to_basic_dict() diff --git a/tests/likelihood/test_weak_lensing.py b/tests/likelihood/test_weak_lensing.py index efc84555a..9c9c7d835 100644 --- a/tests/likelihood/test_weak_lensing.py +++ b/tests/likelihood/test_weak_lensing.py @@ -15,7 +15,7 @@ @pytest.fixture -def mock_tools(): +def mock_tools() -> Mock: """Create a mock ModelingTools object.""" tools = Mock(spec=ModelingTools) mock_cosmo = Mock(spec=pyccl.Cosmology) @@ -34,7 +34,7 @@ def mock_tools(): @pytest.fixture -def sample_tracer_args(): +def sample_tracer_args() -> wl.WeakLensingArgs: """Create sample WeakLensingArgs for testing.""" z = np.linspace(0.1, 2.0, 50) dndz = np.exp(-(((z - 0.5) / 0.2) ** 2)) @@ -49,7 +49,7 @@ def sample_tracer_args(): @pytest.fixture -def mock_sacc_data(): +def mock_sacc_data() -> Mock: """Create a mock SACC data object.""" mock_sacc = Mock(spec=sacc.Sacc) mock_tracer = Mock() @@ -59,14 +59,16 @@ def mock_sacc_data(): return mock_sacc -def test_multiplicative_shear_bias_init(): +def test_multiplicative_shear_bias_init() -> None: """Test MultiplicativeShearBias initialization.""" systematic = wl.MultiplicativeShearBias("test_tracer") assert hasattr(systematic, "mult_bias") assert hasattr(systematic, "parameter_prefix") -def test_multiplicative_shear_bias_apply(mock_tools, sample_tracer_args): +def test_multiplicative_shear_bias_apply( + mock_tools: Mock, sample_tracer_args: wl.WeakLensingArgs +) -> None: """Test MultiplicativeShearBias apply method - covers line 112.""" systematic = wl.MultiplicativeShearBias("test_tracer") # Update parameter values using ParamsMap @@ -82,7 +84,7 @@ def test_multiplicative_shear_bias_apply(mock_tools, sample_tracer_args): assert result.dndz is sample_tracer_args.dndz -def test_linear_alignment_systematic_init_default(): +def test_linear_alignment_systematic_init_default() -> None: """Test LinearAlignmentSystematic default initialization.""" systematic = wl.LinearAlignmentSystematic() assert hasattr(systematic, "ia_bias") @@ -91,7 +93,7 @@ def test_linear_alignment_systematic_init_default(): assert hasattr(systematic, "z_piv") -def test_linear_alignment_systematic_init_with_tracer(): +def test_linear_alignment_systematic_init_with_tracer() -> None: """Test LinearAlignmentSystematic initialization with tracer.""" systematic = wl.LinearAlignmentSystematic("test_tracer", alphag=2.0) assert hasattr(systematic, "ia_bias") @@ -99,8 +101,10 @@ def test_linear_alignment_systematic_init_with_tracer(): @patch("pyccl.growth_factor") def test_linear_alignment_systematic_apply( - mock_growth_factor, mock_tools, sample_tracer_args -): + mock_growth_factor: Mock, + mock_tools: Mock, + sample_tracer_args: wl.WeakLensingArgs, +) -> None: mock_growth_factor.return_value = 0.8 systematic = wl.LinearAlignmentSystematic("test_tracer") @@ -126,7 +130,7 @@ def test_linear_alignment_systematic_apply( mock_growth_factor.assert_called() -def test_massdep_linear_alignment_systematic_init_default(): +def test_massdep_linear_alignment_systematic_init_default() -> None: """Test MassDependentLinearAlignmentSystematic default initialization.""" systematic = wl.MassDependentLinearAlignmentSystematic() assert hasattr(systematic, "ia_amplitude") @@ -135,7 +139,9 @@ def test_massdep_linear_alignment_systematic_init_default(): assert hasattr(systematic, "log10_average_halo_mass") -def test_massdep_linear_alignment_systematic_apply(mock_tools, sample_tracer_args): +def test_massdep_linear_alignment_systematic_apply( + mock_tools: Mock, sample_tracer_args: wl.WeakLensingArgs +) -> None: systematic = wl.MassDependentLinearAlignmentSystematic("test_tracer") # Update parameter values using ParamsMap params = ParamsMap( @@ -156,7 +162,7 @@ def test_massdep_linear_alignment_systematic_apply(mock_tools, sample_tracer_arg assert np.array_equal(result.ia_bias[0], sample_tracer_args.z) -def test_tatt_alignment_systematic_init_default(): +def test_tatt_alignment_systematic_init_default() -> None: """Test TattAlignmentSystematic default initialization.""" systematic = wl.TattAlignmentSystematic() assert hasattr(systematic, "ia_a_1") @@ -164,7 +170,7 @@ def test_tatt_alignment_systematic_init_default(): assert hasattr(systematic, "ia_a_d") -def test_tatt_alignment_systematic_init_with_z_dependence(): +def test_tatt_alignment_systematic_init_with_z_dependence() -> None: """Test TattAlignmentSystematic with z dependence enabled.""" systematic = wl.TattAlignmentSystematic(include_z_dependence=True) assert hasattr(systematic, "ia_a_1") @@ -172,8 +178,10 @@ def test_tatt_alignment_systematic_init_with_z_dependence(): @patch("pyccl.nl_pt.translate_IA_norm") def test_tatt_alignment_systematic_apply( - mock_translate_ia, mock_tools, sample_tracer_args -): + mock_translate_ia: Mock, + mock_tools: Mock, + sample_tracer_args: wl.WeakLensingArgs, +) -> None: """Test TattAlignmentSystematic apply method.""" # Mock the translate_IA_norm function return values c_1 = np.ones_like(sample_tracer_args.z) * 0.1 @@ -212,21 +220,23 @@ def test_tatt_alignment_systematic_apply( assert len(result.ia_pt_c_2) == 2 -def test_hm_alignment_systematic_init(): +def test_hm_alignment_systematic_init() -> None: """Test HMAlignmentSystematic initialization - covers lines 326, 328, 331.""" systematic = wl.HMAlignmentSystematic() assert hasattr(systematic, "ia_a_1h") assert hasattr(systematic, "ia_a_2h") -def test_hm_alignment_systematic_init_with_tracer(): +def test_hm_alignment_systematic_init_with_tracer() -> None: """Test HMAlignmentSystematic initialization with ignored tracer parameter.""" systematic = wl.HMAlignmentSystematic("ignored_tracer") assert hasattr(systematic, "ia_a_1h") assert hasattr(systematic, "ia_a_2h") -def test_hm_alignment_systematic_apply(mock_tools, sample_tracer_args): +def test_hm_alignment_systematic_apply( + mock_tools: Mock, sample_tracer_args: wl.WeakLensingArgs +) -> None: """Test HMAlignmentSystematic apply method - covers line 344.""" systematic = wl.HMAlignmentSystematic() # Update parameter values using ParamsMap @@ -249,14 +259,14 @@ def test_hm_alignment_systematic_apply(mock_tools, sample_tracer_args): class TestWeakLensing: """Test WeakLensing source class.""" - def test_init(self): + def test_init(self) -> None: """Test WeakLensing initialization.""" weak_lensing = wl.WeakLensing(sacc_tracer="test_tracer", scale=2.0) assert weak_lensing.sacc_tracer == "test_tracer" assert weak_lensing.scale == 2.0 assert weak_lensing.current_tracer_args is None - def test_create_ready(self): + def test_create_ready(self) -> None: """Test WeakLensing.create_ready class method.""" zdist = TomographicBin( bin_name="test_bin", @@ -270,7 +280,7 @@ def test_create_ready(self): assert hasattr(weak_lensing, "tracer_args") assert weak_lensing.tracer_args.ia_bias is None - def test_read(self, mock_sacc_data): + def test_read(self, mock_sacc_data: Mock) -> None: """Test WeakLensing _read method.""" weak_lensing = wl.WeakLensing(sacc_tracer="test_tracer") weak_lensing._read(mock_sacc_data) @@ -280,7 +290,7 @@ def test_read(self, mock_sacc_data): mock_sacc_data.get_tracer.assert_called_with("test_tracer") @patch("pyccl.WeakLensingTracer") - def test_create_tracers_basic(self, mock_wl_tracer, mock_tools): + def test_create_tracers_basic(self, mock_wl_tracer: Mock, mock_tools: Mock) -> None: """Test WeakLensing create_tracers with basic configuration.""" mock_ccl_tracer = Mock() mock_wl_tracer.return_value = mock_ccl_tracer @@ -300,7 +310,9 @@ def test_create_tracers_basic(self, mock_wl_tracer, mock_tools): @patch("pyccl.nl_pt.PTIntrinsicAlignmentTracer") @patch("pyccl.WeakLensingTracer") - def test_create_tracers_with_pt(self, mock_wl_tracer, mock_pt_tracer, mock_tools): + def test_create_tracers_with_pt( + self, mock_wl_tracer: Mock, mock_pt_tracer: Mock, mock_tools: Mock + ) -> None: """Test WeakLensing create_tracers with PT - covers lines 450-452, 455.""" mock_ccl_tracer = Mock() mock_wl_tracer.return_value = mock_ccl_tracer @@ -332,8 +344,8 @@ def test_create_tracers_with_pt(self, mock_wl_tracer, mock_pt_tracer, mock_tools @patch("pyccl.halos.SatelliteShearHOD") @patch("pyccl.WeakLensingTracer") def test_create_tracers_with_hm( - self, mock_wl_tracer, mock_halo_profile, mock_tools - ): + self, mock_wl_tracer: Mock, mock_halo_profile: Mock, mock_tools: Mock + ) -> None: """Test WeakLensing create_tracers with HM - covers lines 462, 469, 472.""" mock_ccl_tracer = Mock() mock_wl_tracer.return_value = mock_ccl_tracer @@ -364,7 +376,7 @@ def test_create_tracers_with_hm( assert hasattr(mock_profile, "ia_a_2h") assert mock_profile.ia_a_2h == 1.0 - def test_get_scale(self): + def test_get_scale(self) -> None: """Test WeakLensing get_scale method.""" weak_lensing = wl.WeakLensing(sacc_tracer="test_tracer", scale=2.0) weak_lensing.current_tracer_args = wl.WeakLensingArgs( @@ -375,7 +387,7 @@ def test_get_scale(self): assert weak_lensing.get_scale() == 3.0 - def test_update_with_systematics(self): + def test_update_with_systematics(self) -> None: """Test WeakLensing update method with systematics - covers line 398.""" systematic = wl.MultiplicativeShearBias("test_tracer") weak_lensing = wl.WeakLensing( @@ -389,7 +401,9 @@ def test_update_with_systematics(self): assert weak_lensing.systematics is not None @patch("pyccl.WeakLensingTracer") - def test_create_tracers_with_systematics(self, mock_wl_tracer, mock_tools): + def test_create_tracers_with_systematics( + self, mock_wl_tracer: Mock, mock_tools: Mock + ) -> None: """Test WeakLensing create_tracers with systematics - covers line 421.""" mock_ccl_tracer = Mock() mock_wl_tracer.return_value = mock_ccl_tracer @@ -417,20 +431,20 @@ def test_create_tracers_with_systematics(self, mock_wl_tracer, mock_tools): assert weak_lensing.current_tracer_args.scale == 1.1 # 1.0 * (1 + 0.1) -def test_multiplicative_shear_bias_factory(): +def test_multiplicative_shear_bias_factory() -> None: """Test MultiplicativeShearBiasFactory.""" factory = wl.MultiplicativeShearBiasFactory() bias = factory.create("test_bin") assert isinstance(bias, wl.MultiplicativeShearBias) -def test_multiplicative_shear_bias_factory_global_error(): +def test_multiplicative_shear_bias_factory_global_error() -> None: factory = wl.MultiplicativeShearBiasFactory() with pytest.raises(ValueError, match="MultiplicativeShearBias cannot be global"): factory.create_global() -def test_linear_alignment_factory(): +def test_linear_alignment_factory() -> None: """Test LinearAlignmentSystematicFactory.""" factory = wl.LinearAlignmentSystematicFactory(alphag=2.0) systematic = factory.create("test_bin") @@ -440,7 +454,7 @@ def test_linear_alignment_factory(): assert isinstance(global_systematic, wl.LinearAlignmentSystematic) -def test_tatt_alignment_factory(): +def test_tatt_alignment_factory() -> None: """Test TattAlignmentSystematicFactory.""" factory = wl.TattAlignmentSystematicFactory(include_z_dependence=True) systematic = factory.create("test_bin") @@ -450,7 +464,7 @@ def test_tatt_alignment_factory(): assert isinstance(global_systematic, wl.TattAlignmentSystematic) -def test_weak_lensing_factory(): +def test_weak_lensing_factory() -> None: """Test WeakLensingFactory.""" factory = wl.WeakLensingFactory( per_bin_systematics=[wl.MultiplicativeShearBiasFactory()], @@ -473,7 +487,7 @@ def test_weak_lensing_factory(): assert weak_lensing is wl2 -def test_weak_lensing_factory_from_metadata(): +def test_weak_lensing_factory_from_metadata() -> None: """Test WeakLensingFactory create_from_metadata_only.""" factory = wl.WeakLensingFactory() @@ -486,26 +500,26 @@ def test_weak_lensing_factory_from_metadata(): assert weak_lensing is wl2 -def test_photoz_shift(): +def test_photoz_shift() -> None: """Test PhotoZShift systematic.""" systematic = wl.PhotoZShift("test_tracer") assert hasattr(systematic, "delta_z") -def test_photoz_shift_and_stretch(): +def test_photoz_shift_and_stretch() -> None: """Test PhotoZShiftandStretch systematic.""" systematic = wl.PhotoZShiftandStretch("test_tracer") assert hasattr(systematic, "delta_z") assert hasattr(systematic, "sigma_z") -def test_select_field(): +def test_select_field() -> None: """Test SelectField systematic.""" systematic = wl.SelectField() assert hasattr(systematic, "field") -def test_weak_lensing_args_immutable(): +def test_weak_lensing_args_immutable() -> None: """Test that WeakLensingArgs is immutable (frozen dataclass).""" args = wl.WeakLensingArgs( z=np.array([0.1, 0.5, 1.0]), @@ -520,7 +534,7 @@ def test_weak_lensing_args_immutable(): args.scale = 2.0 # type: ignore[misc] -def test_systematic_with_none_tracer(): +def test_systematic_with_none_tracer() -> None: """Test systematics with None tracer name.""" # LinearAlignmentSystematic should accept None tracer systematic = wl.LinearAlignmentSystematic(sacc_tracer=None) @@ -532,7 +546,9 @@ def test_systematic_with_none_tracer(): @patch("pyccl.growth_factor") -def test_linear_alignment_complex_calculation(mock_growth_factor, mock_tools): +def test_linear_alignment_complex_calculation( + mock_growth_factor: Mock, mock_tools: Mock +) -> None: """Test LinearAlignmentSystematic with complex parameter values.""" mock_growth_factor.return_value = np.array([0.7, 0.8, 0.9]) diff --git a/tests/metadata/test_bin_pair_selector.py b/tests/metadata/test_bin_pair_selector.py index 2dcc11ac4..959959d51 100644 --- a/tests/metadata/test_bin_pair_selector.py +++ b/tests/metadata/test_bin_pair_selector.py @@ -15,7 +15,7 @@ from firecrown.metadata_functions import make_binned_two_point_filtered -def test_pair_selector_register_missing_kind(): +def test_pair_selector_register_missing_kind() -> None: with pytest.raises( ValueError, match="MissingBinPairSelector has no default for 'kind'" ): @@ -32,7 +32,7 @@ def keep( _ = MissingBinPairSelector(kind="foo") -def test_pair_selector_register_duplicate_kind(): +def test_pair_selector_register_duplicate_kind() -> None: with pytest.raises(ValueError, match="Duplicate pair selector kind foo"): @mt.register_bin_pair_selector @@ -61,7 +61,7 @@ def keep( _ = Duplicate1BinPairSelector() -def test_pair_selector_auto(all_harmonic_bins): +def test_pair_selector_auto(all_harmonic_bins: list[mt.TomographicBin]) -> None: auto_pair_selector = ( mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() ) @@ -77,7 +77,7 @@ def test_pair_selector_auto(all_harmonic_bins): assert two_point_xy.x_measurement == two_point_xy.y_measurement -def test_pair_selector_auto_source(all_harmonic_bins): +def test_pair_selector_auto_source(all_harmonic_bins: list[mt.TomographicBin]) -> None: auto_pair_selector = ( mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() ) @@ -94,7 +94,7 @@ def test_pair_selector_auto_source(all_harmonic_bins): assert two_point_xy.x_measurement in mt.GALAXY_SOURCE_TYPES -def test_pair_selector_auto_lens(all_harmonic_bins): +def test_pair_selector_auto_lens(all_harmonic_bins: list[mt.TomographicBin]) -> None: auto_pair_selector = ( mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() ) @@ -111,7 +111,9 @@ def test_pair_selector_auto_lens(all_harmonic_bins): assert two_point_xy.x_measurement in mt.GALAXY_LENS_TYPES -def test_pair_selector_auto_source_lens(all_harmonic_bins): +def test_pair_selector_auto_source_lens( + all_harmonic_bins: list[mt.TomographicBin], +) -> None: auto_pair_selector = ( mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() ) @@ -131,7 +133,7 @@ def test_pair_selector_auto_source_lens(all_harmonic_bins): assert two_point_xy.x_measurement == two_point_xy.y_measurement -def test_pair_selector_named(all_harmonic_bins): +def test_pair_selector_named(all_harmonic_bins: list[mt.TomographicBin]) -> None: named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1_1", "bin_1_2")]) two_point_xy_combinations = make_binned_two_point_filtered( @@ -146,7 +148,7 @@ def test_pair_selector_named(all_harmonic_bins): } -def test_pair_selector_type_source(all_harmonic_bins): +def test_pair_selector_type_source(all_harmonic_bins: list[mt.TomographicBin]) -> None: type_source_pair_selector = mt.TypeSourceBinPairSelector( type_source=mt.TypeSource.DEFAULT ) @@ -176,7 +178,7 @@ def test_pair_selector_type_source(all_harmonic_bins): assert len(two_point_xy_combinations) == 1 -def test_pair_selector_not_named(all_harmonic_bins): +def test_pair_selector_not_named(all_harmonic_bins: list[mt.TomographicBin]) -> None: named_pair_selector = mt.NamedBinPairSelector(names=[("bin_1_1", "bin_1_2")]) two_point_xy_combinations = make_binned_two_point_filtered( @@ -191,7 +193,9 @@ def test_pair_selector_not_named(all_harmonic_bins): ] -def test_pair_selector_first_neighbor(many_harmonic_bins): +def test_pair_selector_first_neighbor( + many_harmonic_bins: list[mt.TomographicBin], +) -> None: first_neighbor_pair_selector = mt.AutoNameDiffBinPairSelector( neighbors_diff=[0, 1, -1] ) @@ -221,7 +225,9 @@ def test_pair_selector_first_neighbor(many_harmonic_bins): assert abs(index1 - index2) <= 1 -def test_pair_selector_first_neighbor_no_auto(many_harmonic_bins): +def test_pair_selector_first_neighbor_no_auto( + many_harmonic_bins: list[mt.TomographicBin], +) -> None: first_neighbor_pair_selector = mt.AutoNameDiffBinPairSelector( neighbors_diff=[1, -1] ) @@ -243,7 +249,7 @@ def test_pair_selector_first_neighbor_no_auto(many_harmonic_bins): assert abs(index1 - index2) == 1 -def test_pair_selector_auto_name_keep(): +def test_pair_selector_auto_name_keep() -> None: z1 = mt.TomographicBin( bin_name="bin_1", z=np.array([0.1]), @@ -261,7 +267,7 @@ def test_pair_selector_auto_name_keep(): assert not rule.keep((z1, z2), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_pair_selector_auto_measurement_keep(): +def test_pair_selector_auto_measurement_keep() -> None: z1 = mt.TomographicBin( bin_name="bin_1", z=np.array([0.1]), @@ -279,7 +285,7 @@ def test_pair_selector_auto_measurement_keep(): assert not rule.keep((z1, z2), (mt.Galaxies.COUNTS, mt.Galaxies.SHEAR_E)) -def test_pair_selector_named_keep(): +def test_pair_selector_named_keep() -> None: z1 = mt.TomographicBin( bin_name="bin_1", z=np.array([0.1]), @@ -296,7 +302,7 @@ def test_pair_selector_named_keep(): assert rule.keep((z1, z2), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_pair_selector_lens_keep(): +def test_pair_selector_lens_keep() -> None: z1 = mt.TomographicBin( bin_name="bin_1", z=np.array([0.1]), @@ -314,7 +320,7 @@ def test_pair_selector_lens_keep(): assert not rule.keep((z1, z2), (mt.Galaxies.SHEAR_E, mt.Galaxies.SHEAR_E)) -def test_pair_selector_source_keep(): +def test_pair_selector_source_keep() -> None: z1 = mt.TomographicBin( bin_name="src1", z=np.array([0.1]), @@ -331,7 +337,7 @@ def test_pair_selector_source_keep(): assert rule.keep((z1, z2), (mt.Galaxies.SHEAR_E, mt.Galaxies.SHEAR_E)) -def test_pair_selector_first_neighbor_mixed(): +def test_pair_selector_first_neighbor_mixed() -> None: z1 = mt.TomographicBin( bin_name="extra_src_a", z=np.array([0.1]), @@ -359,7 +365,7 @@ def test_pair_selector_first_neighbor_mixed(): assert rule.keep((z3, z3), (mt.Galaxies.SHEAR_E, mt.Galaxies.SHEAR_E)) -def test_pair_selector_serialization_and_or(): +def test_pair_selector_serialization_and_or() -> None: pair_selector = ( mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() @@ -382,7 +388,7 @@ def test_pair_selector_serialization_and_or(): assert pair_selector.model_dump() == pair_selector_from_yaml.model_dump() -def test_pair_selector_serialization_simple_and(): +def test_pair_selector_serialization_simple_and() -> None: pair_selector = mt.AutoNameBinPairSelector() & mt.AutoMeasurementBinPairSelector() yaml_str = yaml.dump(pair_selector.model_dump(), sort_keys=False) pair_selector_from_yaml = mt.BinPairSelector.model_validate( @@ -392,7 +398,7 @@ def test_pair_selector_serialization_simple_and(): assert pair_selector.model_dump() == pair_selector_from_yaml.model_dump() -def test_pair_selector_serialization_simple_or(): +def test_pair_selector_serialization_simple_or() -> None: pair_selector = mt.AutoNameBinPairSelector() | mt.LensBinPairSelector() yaml_str = yaml.dump(pair_selector.model_dump(), sort_keys=False) pair_selector_from_yaml = mt.BinPairSelector.model_validate( @@ -402,7 +408,7 @@ def test_pair_selector_serialization_simple_or(): assert pair_selector.model_dump() == pair_selector_from_yaml.model_dump() -def test_pair_selector_serialization_nested_and_or(): +def test_pair_selector_serialization_nested_and_or() -> None: pair_selector = ( (mt.AutoNameBinPairSelector() & mt.LensBinPairSelector()) | ( @@ -421,7 +427,7 @@ def test_pair_selector_serialization_nested_and_or(): assert pair_selector.model_dump() == pair_selector_from_yaml.model_dump() -def test_pair_selector_serialization_negation(): +def test_pair_selector_serialization_negation() -> None: base_rule = mt.AutoNameBinPairSelector() neg_rule = ~base_rule yaml_str = yaml.dump(neg_rule.model_dump(), sort_keys=False) @@ -430,7 +436,7 @@ def test_pair_selector_serialization_negation(): assert neg_rule.model_dump() == rule_from_yaml.model_dump() -def test_pair_selector_serialization_double_negation(): +def test_pair_selector_serialization_double_negation() -> None: base_rule = mt.AutoNameBinPairSelector() double_neg_rule = ~~base_rule yaml_str = yaml.dump(double_neg_rule.model_dump(), sort_keys=False) @@ -439,7 +445,7 @@ def test_pair_selector_serialization_double_negation(): assert double_neg_rule.model_dump() == rule_from_yaml.model_dump() -def test_pair_selector_serialization_and_not(): +def test_pair_selector_serialization_and_not() -> None: rule = mt.AutoNameBinPairSelector() & ~mt.LensBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) rule_from_yaml = mt.BinPairSelector.model_validate(yaml.safe_load(yaml_str)) @@ -447,7 +453,7 @@ def test_pair_selector_serialization_and_not(): assert rule.model_dump() == rule_from_yaml.model_dump() -def test_pair_selector_serialization_or_not(): +def test_pair_selector_serialization_or_not() -> None: rule = mt.AutoNameBinPairSelector() | ~mt.LensBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) rule_from_yaml = mt.BinPairSelector.model_validate(yaml.safe_load(yaml_str)) @@ -456,7 +462,7 @@ def test_pair_selector_serialization_or_not(): assert rule.model_dump() == rule_from_yaml.model_dump() -def test_pair_selector_serialization_type_source(): +def test_pair_selector_serialization_type_source() -> None: rule = mt.TypeSourceBinPairSelector(type_source=mt.TypeSource("NewTypeSource")) yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) rule_from_yaml = mt.BinPairSelector.model_validate(yaml.safe_load(yaml_str)) @@ -466,7 +472,7 @@ def test_pair_selector_serialization_type_source(): assert rule.type_source == rule_from_yaml.type_source -def test_pair_selector_deserialization_lens(): +def test_pair_selector_deserialization_lens() -> None: yaml_str = """ kind: lens """ @@ -474,7 +480,7 @@ def test_pair_selector_deserialization_lens(): assert isinstance(rule, mt.LensBinPairSelector) -def test_pair_selector_deserialization_source(): +def test_pair_selector_deserialization_source() -> None: yaml_str = """ kind: source """ @@ -482,7 +488,7 @@ def test_pair_selector_deserialization_source(): assert isinstance(rule, mt.SourceBinPairSelector) -def test_pair_selector_deserialization_named(): +def test_pair_selector_deserialization_named() -> None: yaml_str = """ kind: named names: @@ -493,7 +499,7 @@ def test_pair_selector_deserialization_named(): assert rule.names == [("bin_1", "bin_2")] -def test_pair_selector_deserialization_auto_name(): +def test_pair_selector_deserialization_auto_name() -> None: yaml_str = """ kind: auto-name """ @@ -501,7 +507,7 @@ def test_pair_selector_deserialization_auto_name(): assert isinstance(rule, mt.AutoNameBinPairSelector) -def test_pair_selector_deserialization_auto_measurement(): +def test_pair_selector_deserialization_auto_measurement() -> None: yaml_str = """ kind: auto-measurement """ @@ -509,7 +515,7 @@ def test_pair_selector_deserialization_auto_measurement(): assert isinstance(rule, mt.AutoMeasurementBinPairSelector) -def test_pair_selector_deserialization_not(): +def test_pair_selector_deserialization_not() -> None: yaml_str = """ kind: not pair_selector: @@ -520,7 +526,7 @@ def test_pair_selector_deserialization_not(): assert isinstance(rule.pair_selector, mt.LensBinPairSelector) -def test_pair_selector_deserialization_and(): +def test_pair_selector_deserialization_and() -> None: yaml_str = """ kind: and pair_selectors: @@ -536,7 +542,7 @@ def test_pair_selector_deserialization_and(): assert isinstance(rule.pair_selectors[2], mt.SourceBinPairSelector) -def test_pair_selector_deserialization_or(): +def test_pair_selector_deserialization_or() -> None: yaml_str = """ kind: or pair_selectors: @@ -554,7 +560,7 @@ def test_pair_selector_deserialization_or(): assert isinstance(rule.pair_selectors[3], mt.AutoMeasurementBinPairSelector) -def test_pair_selector_deserialization_nested_and_or(): +def test_pair_selector_deserialization_nested_and_or() -> None: yaml_str = """ kind: and pair_selectors: @@ -580,7 +586,7 @@ def test_pair_selector_deserialization_nested_and_or(): assert isinstance(rule.pair_selectors[1], mt.AutoMeasurementBinPairSelector) -def test_pair_selector_deserialization_nested_not(): +def test_pair_selector_deserialization_nested_not() -> None: yaml_str = """ kind: not pair_selector: @@ -599,7 +605,7 @@ def test_pair_selector_deserialization_nested_not(): assert isinstance(rule.pair_selector.pair_selectors[2], mt.SourceBinPairSelector) -def test_pair_selector_deserialization_nested_or_and(): +def test_pair_selector_deserialization_nested_or_and() -> None: yaml_str = """ kind: or pair_selectors: @@ -625,7 +631,7 @@ def test_pair_selector_deserialization_nested_or_and(): assert isinstance(rule.pair_selectors[1], mt.AutoMeasurementBinPairSelector) -def test_pair_selector_deserialization_invalid_kind(): +def test_pair_selector_deserialization_invalid_kind() -> None: yaml_str = """ kind: not_a_valid_kind """ @@ -638,7 +644,7 @@ def test_pair_selector_deserialization_invalid_kind(): # ============================================================================ -def test_composite_selector_bad_selector_not_initialized(): +def test_composite_selector_bad_selector_not_initialized() -> None: """Test that CompositeSelector raises NotImplementedError when _impl is not set.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -671,7 +677,7 @@ def model_post_init(self, _: Any, /) -> None: bad_composite.keep((z1, z2), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_auto_bin_pair_selector(all_harmonic_bins): +def test_auto_bin_pair_selector(all_harmonic_bins: list[mt.TomographicBin]) -> None: """Test AutoBinPairSelector composite selector.""" auto_pair_selector = mt.AutoBinPairSelector() @@ -686,7 +692,7 @@ def test_auto_bin_pair_selector(all_harmonic_bins): assert two_point_xy.x_measurement == two_point_xy.y_measurement -def test_auto_bin_pair_selector_keep(): +def test_auto_bin_pair_selector_keep() -> None: """Test AutoBinPairSelector keep method.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -709,7 +715,7 @@ def test_auto_bin_pair_selector_keep(): assert not rule.keep((z1, z1), (mt.Galaxies.COUNTS, mt.Galaxies.SHEAR_E)) -def test_source_lens_pair_selector(): +def test_source_lens_pair_selector() -> None: """Test SourceLensBinPairSelector with order-dependent matching.""" source_lens_pair_selector = mt.SourceLensBinPairSelector() @@ -742,7 +748,7 @@ def test_source_lens_pair_selector(): assert two_point_xy.y_measurement == mt.Galaxies.COUNTS -def test_source_lens_pair_selector_keep(): +def test_source_lens_pair_selector_keep() -> None: """Test SourceLensBinPairSelector keep method with fixed order.""" z_source = mt.TomographicBin( bin_name="src1", @@ -770,7 +776,7 @@ def test_source_lens_pair_selector_keep(): assert not rule.keep((z_lens, z_lens), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_auto_bin_pair_selector_serialization(): +def test_auto_bin_pair_selector_serialization() -> None: """Test serialization of AutoBinPairSelector.""" rule = mt.AutoBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) @@ -779,7 +785,7 @@ def test_auto_bin_pair_selector_serialization(): assert rule == rule_from_yaml -def test_source_lens_pair_selector_serialization(): +def test_source_lens_pair_selector_serialization() -> None: """Test serialization of SourceLensBinPairSelector.""" rule = mt.SourceLensBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) @@ -788,7 +794,7 @@ def test_source_lens_pair_selector_serialization(): assert rule == rule_from_yaml -def test_auto_bin_pair_selector_deserialization(): +def test_auto_bin_pair_selector_deserialization() -> None: """Test deserialization of AutoBinPairSelector.""" yaml_str = """ kind: auto-bin @@ -797,7 +803,7 @@ def test_auto_bin_pair_selector_deserialization(): assert isinstance(rule, mt.AutoBinPairSelector) -def test_source_lens_pair_selector_deserialization(): +def test_source_lens_pair_selector_deserialization() -> None: """Test deserialization of SourceLensBinPairSelector.""" yaml_str = """ kind: source-lens @@ -811,7 +817,7 @@ def test_source_lens_pair_selector_deserialization(): # ============================================================================ -def test_cross_name_bin_pair_selector_keep(): +def test_cross_name_bin_pair_selector_keep() -> None: """Test CrossNameBinPairSelector keep method.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -832,7 +838,9 @@ def test_cross_name_bin_pair_selector_keep(): assert not rule.keep((z1, z1), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_cross_name_bin_pair_selector(all_harmonic_bins): +def test_cross_name_bin_pair_selector( + all_harmonic_bins: list[mt.TomographicBin], +) -> None: """Test CrossNameBinPairSelector filters out auto-name correlations.""" cross_name_selector = mt.CrossNameBinPairSelector() @@ -847,7 +855,7 @@ def test_cross_name_bin_pair_selector(all_harmonic_bins): assert two_point_xy.x.bin_name != two_point_xy.y.bin_name -def test_cross_measurement_bin_pair_selector_keep(): +def test_cross_measurement_bin_pair_selector_keep() -> None: """Test CrossMeasurementBinPairSelector keep method.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -868,7 +876,9 @@ def test_cross_measurement_bin_pair_selector_keep(): assert not rule.keep((z1, z2), (mt.Galaxies.COUNTS, mt.Galaxies.COUNTS)) -def test_cross_measurement_bin_pair_selector(all_harmonic_bins): +def test_cross_measurement_bin_pair_selector( + all_harmonic_bins: list[mt.TomographicBin], +) -> None: """Test CrossMeasurementBinPairSelector. Test filters out auto-measurement correlations.""" @@ -883,7 +893,7 @@ def test_cross_measurement_bin_pair_selector(all_harmonic_bins): assert two_point_xy.x_measurement != two_point_xy.y_measurement -def test_cross_bin_pair_selector_keep(): +def test_cross_bin_pair_selector_keep() -> None: """Test CrossBinPairSelector keep method.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -907,7 +917,7 @@ def test_cross_bin_pair_selector_keep(): assert rule.keep((z1, z1), (mt.Galaxies.COUNTS, mt.Galaxies.SHEAR_E)) -def test_cross_bin_pair_selector(all_harmonic_bins): +def test_cross_bin_pair_selector(all_harmonic_bins: list[mt.TomographicBin]) -> None: """Test CrossBinPairSelector filters out auto-bin correlations.""" cross_bin_selector = mt.CrossBinPairSelector() @@ -925,7 +935,7 @@ def test_cross_bin_pair_selector(all_harmonic_bins): assert not is_auto_bin -def test_cross_name_bin_pair_selector_serialization(): +def test_cross_name_bin_pair_selector_serialization() -> None: """Test serialization of CrossNameBinPairSelector.""" rule = mt.CrossNameBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) @@ -934,7 +944,7 @@ def test_cross_name_bin_pair_selector_serialization(): assert rule == rule_from_yaml -def test_cross_measurement_bin_pair_selector_serialization(): +def test_cross_measurement_bin_pair_selector_serialization() -> None: """Test serialization of CrossMeasurementBinPairSelector.""" rule = mt.CrossMeasurementBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) @@ -943,7 +953,7 @@ def test_cross_measurement_bin_pair_selector_serialization(): assert rule == rule_from_yaml -def test_cross_bin_pair_selector_serialization(): +def test_cross_bin_pair_selector_serialization() -> None: """Test serialization of CrossBinPairSelector.""" rule = mt.CrossBinPairSelector() yaml_str = yaml.dump(rule.model_dump(), sort_keys=False) @@ -952,7 +962,7 @@ def test_cross_bin_pair_selector_serialization(): assert rule == rule_from_yaml -def test_cross_name_bin_pair_selector_deserialization(): +def test_cross_name_bin_pair_selector_deserialization() -> None: """Test deserialization of CrossNameBinPairSelector.""" yaml_str = """ kind: cross-name @@ -961,7 +971,7 @@ def test_cross_name_bin_pair_selector_deserialization(): assert isinstance(rule, mt.CrossNameBinPairSelector) -def test_cross_measurement_bin_pair_selector_deserialization(): +def test_cross_measurement_bin_pair_selector_deserialization() -> None: """Test deserialization of CrossMeasurementBinPairSelector.""" yaml_str = """ kind: cross-measurement @@ -970,7 +980,7 @@ def test_cross_measurement_bin_pair_selector_deserialization(): assert isinstance(rule, mt.CrossMeasurementBinPairSelector) -def test_cross_bin_pair_selector_deserialization(): +def test_cross_bin_pair_selector_deserialization() -> None: """Test deserialization of CrossBinPairSelector.""" yaml_str = """ kind: cross-bin @@ -979,7 +989,7 @@ def test_cross_bin_pair_selector_deserialization(): assert isinstance(rule, mt.CrossBinPairSelector) -def test_cross_selectors_are_inverses(): +def test_cross_selectors_are_inverses() -> None: """Test that Cross selectors are exact inverses of Auto selectors.""" z1 = mt.TomographicBin( bin_name="bin_1", @@ -1032,7 +1042,7 @@ def test_cross_selectors_are_inverses(): # ============================================================================ -def test_three_two_bin_pair_selector_keep(): +def test_three_two_bin_pair_selector_keep() -> None: """Test ThreeTwoBinPairSelector keep method with various combinations.""" selector = mt.ThreeTwoBinPairSelector() @@ -1086,7 +1096,9 @@ def test_three_two_bin_pair_selector_keep(): ) -def test_three_two_bin_pair_selector_integration(all_harmonic_bins): +def test_three_two_bin_pair_selector_integration( + all_harmonic_bins: list[mt.TomographicBin], +) -> None: """Test ThreeTwoBinPairSelector with realistic bin structure.""" selector = mt.ThreeTwoBinPairSelector() @@ -1141,7 +1153,7 @@ def test_three_two_bin_pair_selector_integration(all_harmonic_bins): assert source_lens_auto_count == 0, "Should NOT have same-bin source-lens pairs" -def test_three_two_bin_pair_selector_components(): +def test_three_two_bin_pair_selector_components() -> None: """Test that ThreeTwoBinPairSelector includes the right components.""" selector = mt.ThreeTwoBinPairSelector() @@ -1197,7 +1209,7 @@ def test_three_two_bin_pair_selector_components(): assert not has_shear_counts_auto, "Should NOT include same-bin source-lens" -def test_three_two_bin_pair_selector_serialization(): +def test_three_two_bin_pair_selector_serialization() -> None: """Test serialization of ThreeTwoBinPairSelector.""" selector = mt.ThreeTwoBinPairSelector() yaml_str = yaml.dump(selector.model_dump(), sort_keys=False) @@ -1206,7 +1218,7 @@ def test_three_two_bin_pair_selector_serialization(): assert selector == selector_from_yaml -def test_three_two_bin_pair_selector_deserialization(): +def test_three_two_bin_pair_selector_deserialization() -> None: """Test deserialization of ThreeTwoBinPairSelector.""" yaml_str = """ kind: 3x2pt @@ -1215,7 +1227,7 @@ def test_three_two_bin_pair_selector_deserialization(): assert isinstance(selector, mt.ThreeTwoBinPairSelector) -def test_three_two_bin_pair_selector_distance_parameters(): +def test_three_two_bin_pair_selector_distance_parameters() -> None: """Test distance parameters for ThreeTwoBinPairSelector.""" selector = mt.ThreeTwoBinPairSelector( source_dist=1, lens_dist=1, source_lens_dist=2 diff --git a/tests/metadata/test_cmb_tracer.py b/tests/metadata/test_cmb_tracer.py index 2a572bb1e..8f8741cf2 100644 --- a/tests/metadata/test_cmb_tracer.py +++ b/tests/metadata/test_cmb_tracer.py @@ -13,7 +13,7 @@ def make_tomobin() -> TomographicBin: ) -def test_cmblensing_basic(): +def test_cmblensing_basic() -> None: cmb = CMBLensing(bin_name="cmb0", z_lss=1090.0) assert cmb.bin_name == "cmb0" assert cmb.z_lss == 1090.0 @@ -21,7 +21,7 @@ def test_cmblensing_basic(): assert cmb.measurement_list == [CMB.CONVERGENCE] -def test_cmblensing_validation(): +def test_cmblensing_validation() -> None: with pytest.raises(ValueError, match="bin_name should not be empty"): CMBLensing(bin_name="", z_lss=1090.0) @@ -37,7 +37,7 @@ def test_cmblensing_validation(): CMBLensing(bin_name="cmb0", z_lss=1100.0, measurements={Galaxies.COUNTS}) -def test_tomographicbin_equality_and_cmb_mismatch(): +def test_tomographicbin_equality_and_cmb_mismatch() -> None: tb1 = make_tomobin() tb2 = TomographicBin( bin_name="src0", @@ -58,7 +58,7 @@ def test_tomographicbin_equality_and_cmb_mismatch(): assert (cmb == object()) is False -def test_cmblensing_equality(): +def test_cmblensing_equality() -> None: c1 = CMBLensing(bin_name="cmb0", z_lss=1090.0) c2 = CMBLensing(bin_name="cmb0", z_lss=1090.0) assert c1 == c2 diff --git a/tests/metadata/test_combination_utils.py b/tests/metadata/test_combination_utils.py index da7e27ea0..58db5fd72 100644 --- a/tests/metadata/test_combination_utils.py +++ b/tests/metadata/test_combination_utils.py @@ -22,7 +22,7 @@ def fixture_sample_combinations( return make_all_photoz_bin_combinations(all_harmonic_bins) -def test_filter_two_point_combinations_empty_input(): +def test_filter_two_point_combinations_empty_input() -> None: """Test filtering with an empty input list.""" selector = mt.AutoNameBinPairSelector() result = filter_two_point_combinations([], selector) @@ -31,7 +31,7 @@ def test_filter_two_point_combinations_empty_input(): def test_filter_two_point_combinations_all_pass( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering where all combinations pass the selector.""" # Create a selector that accepts everything # Using OR of complementary selectors @@ -46,7 +46,7 @@ def test_filter_two_point_combinations_all_pass( def test_filter_two_point_combinations_none_pass( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering where no combinations pass the selector.""" # Create a selector that rejects everything by using contradictory conditions selector = mt.AutoNameBinPairSelector() & mt.CrossNameBinPairSelector() @@ -60,7 +60,7 @@ def test_filter_two_point_combinations_none_pass( def test_filter_two_point_combinations_auto_name( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with AutoNameBinPairSelector.""" selector = mt.AutoNameBinPairSelector() @@ -78,7 +78,7 @@ def test_filter_two_point_combinations_auto_name( def test_filter_two_point_combinations_cross_name( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with CrossNameBinPairSelector.""" selector = mt.CrossNameBinPairSelector() @@ -96,7 +96,7 @@ def test_filter_two_point_combinations_cross_name( def test_filter_two_point_combinations_source_selector( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with SourceBinPairSelector.""" selector = mt.SourceBinPairSelector() @@ -111,7 +111,7 @@ def test_filter_two_point_combinations_source_selector( def test_filter_two_point_combinations_lens_selector( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with LensBinPairSelector.""" selector = mt.LensBinPairSelector() @@ -126,7 +126,7 @@ def test_filter_two_point_combinations_lens_selector( def test_filter_two_point_combinations_composite_and( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with composite AND selector.""" selector = mt.AutoNameBinPairSelector() & mt.SourceBinPairSelector() @@ -144,7 +144,7 @@ def test_filter_two_point_combinations_composite_and( def test_filter_two_point_combinations_composite_or( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with composite OR selector.""" selector = (mt.AutoNameBinPairSelector() & mt.SourceBinPairSelector()) | ( mt.AutoNameBinPairSelector() & mt.LensBinPairSelector() @@ -171,7 +171,7 @@ def test_filter_two_point_combinations_composite_or( def test_filter_two_point_combinations_not_selector( sample_combinations: list[mt.TwoPointXY], -): +) -> None: """Test filtering with NOT selector.""" selector = ~mt.AutoNameBinPairSelector() @@ -182,7 +182,7 @@ def test_filter_two_point_combinations_not_selector( assert combo.x.bin_name != combo.y.bin_name -def test_filter_two_point_combinations_named_selector(): +def test_filter_two_point_combinations_named_selector() -> None: """Test filtering with NamedBinPairSelector.""" # Create specific bins for this test bin1 = mt.TomographicBin( @@ -217,7 +217,7 @@ def test_filter_two_point_combinations_named_selector(): assert result[0].y.bin_name == "bin_2" -def test_filter_two_point_combinations_preserves_order(): +def test_filter_two_point_combinations_preserves_order() -> None: """Test that filtering preserves the original order of combinations.""" # Create bins with specific names to control ordering bins = [ @@ -248,7 +248,7 @@ def test_filter_two_point_combinations_preserves_order(): assert result == expected_order -def test_filter_two_point_combinations_auto_measurement(): +def test_filter_two_point_combinations_auto_measurement() -> None: """Test filtering with AutoMeasurementBinPairSelector.""" bin1 = mt.TomographicBin( bin_name="bin_1", @@ -273,7 +273,7 @@ def test_filter_two_point_combinations_auto_measurement(): assert combo.x_measurement == combo.y_measurement -def test_filter_two_point_combinations_cross_measurement(): +def test_filter_two_point_combinations_cross_measurement() -> None: """Test filtering with CrossMeasurementBinPairSelector.""" bin1 = mt.TomographicBin( bin_name="bin_1", @@ -292,7 +292,7 @@ def test_filter_two_point_combinations_cross_measurement(): assert combo.x_measurement != combo.y_measurement -def test_filter_two_point_combinations_complex_selector(): +def test_filter_two_point_combinations_complex_selector() -> None: """Test filtering with a complex nested selector.""" bins = [ mt.TomographicBin( @@ -329,7 +329,7 @@ def test_filter_two_point_combinations_complex_selector(): assert auto_source or cross_lens -def test_filter_two_point_combinations_triple_negation(): +def test_filter_two_point_combinations_triple_negation() -> None: """Test filtering with triple negation selector.""" bins = [ mt.TomographicBin( @@ -352,7 +352,7 @@ def test_filter_two_point_combinations_triple_negation(): assert combo.x.bin_name != combo.y.bin_name -def test_filter_two_point_combinations_with_real_vs_harmonic(): +def test_filter_two_point_combinations_with_real_vs_harmonic() -> None: """Test that filter_two_point_combinations. Test that filter_two_point_combinations works with different measurement @@ -378,7 +378,7 @@ def test_filter_two_point_combinations_with_real_vs_harmonic(): assert result[0].y_measurement == mt.Galaxies.SHEAR_E -def test_filter_two_point_combinations_left_right_measurement_selectors(): +def test_filter_two_point_combinations_left_right_measurement_selectors() -> None: """Test filtering with SourceLensBinPairSelector.""" bin1 = mt.TomographicBin( bin_name="src", @@ -408,7 +408,7 @@ def test_filter_two_point_combinations_left_right_measurement_selectors(): assert result[0].y_measurement in mt.GALAXY_LENS_TYPES -def test_filter_two_point_combinations_idempotent(): +def test_filter_two_point_combinations_idempotent() -> None: """Test that applying the same filter twice gives the same result.""" bins = [ mt.TomographicBin( @@ -430,7 +430,7 @@ def test_filter_two_point_combinations_idempotent(): assert result1 == result2 -def test_filter_two_point_combinations_commutative_and(): +def test_filter_two_point_combinations_commutative_and() -> None: """Test that AND selector is commutative.""" bins = [ mt.TomographicBin( @@ -453,7 +453,7 @@ def test_filter_two_point_combinations_commutative_and(): assert result1 == result2 -def test_filter_two_point_combinations_commutative_or(): +def test_filter_two_point_combinations_commutative_or() -> None: """Test that OR selector is commutative.""" bins = [ mt.TomographicBin( diff --git a/tests/metadata/test_data_functions.py b/tests/metadata/test_data_functions.py index 6bb960c8f..1838e25d5 100644 --- a/tests/metadata/test_data_functions.py +++ b/tests/metadata/test_data_functions.py @@ -111,7 +111,9 @@ def fixture_real_bins( for elem in it.combinations(ALL_HARMONIC_BINS, ncomb) ], ) -def fixture_harmonic_filter_collection(request) -> TwoPointBinFilterCollection: +def fixture_harmonic_filter_collection( + request: pytest.FixtureRequest, +) -> TwoPointBinFilterCollection: """Create a TwoPointBinFilterCollection with harmonic filters.""" bin_filters = [ TwoPointBinFilter.from_args(name_a, m_a, name_b, m_b, 20, 60) @@ -137,7 +139,9 @@ def fixture_harmonic_filter_collection(request) -> TwoPointBinFilterCollection: for elem in it.combinations(ALL_REAL_BINS, ncomb) ], ) -def fixture_real_filter_collection(request) -> TwoPointBinFilterCollection: +def fixture_real_filter_collection( + request: pytest.FixtureRequest, +) -> TwoPointBinFilterCollection: """Create a TwoPointBinFilterCollection with real filters.""" bin_filters = [ TwoPointBinFilter.from_args(name_a, m_a, name_b, m_b, 0.1, 0.5) @@ -147,7 +151,7 @@ def fixture_real_filter_collection(request) -> TwoPointBinFilterCollection: return TwoPointBinFilterCollection(filters=bin_filters) -def test_two_point_bin_filter_construct(): +def test_two_point_bin_filter_construct() -> None: bin_spec = [ TwoPointTracerSpec(name="bin_1", measurement=Galaxies.COUNTS), TwoPointTracerSpec(name="bin_2", measurement=Galaxies.SHEAR_E), @@ -164,7 +168,7 @@ def test_two_point_bin_filter_construct(): assert bin_filter_from_args.interval == (0.1, 0.5) -def test_two_point_bin_filter_construct_auto(): +def test_two_point_bin_filter_construct_auto() -> None: bin_spec = [TwoPointTracerSpec(name="bin_1", measurement=Galaxies.COUNTS)] bin_filter = TwoPointBinFilter(spec=bin_spec, interval=(0.1, 0.5)) assert bin_filter.spec == bin_spec @@ -178,7 +182,7 @@ def test_two_point_bin_filter_construct_auto(): assert bin_filter_from_args.interval == (0.1, 0.5) -def test_two_point_bin_filter_construct_invalid_range(): +def test_two_point_bin_filter_construct_invalid_range() -> None: bin_spec = [ TwoPointTracerSpec(name="bin_1", measurement=Galaxies.COUNTS), TwoPointTracerSpec(name="bin_2", measurement=Galaxies.SHEAR_E), @@ -189,14 +193,14 @@ def test_two_point_bin_filter_construct_invalid_range(): TwoPointBinFilter(spec=bin_spec, interval=(0.5, 0.1)) -def test_two_point_bin_filter_construct_empty_spec(): +def test_two_point_bin_filter_construct_empty_spec() -> None: with pytest.raises( ValueError, match="The bin_spec must contain one or two elements." ): TwoPointBinFilter(spec=[], interval=(0.1, 0.5)) -def test_two_point_bin_filter_construct_too_many_spec(): +def test_two_point_bin_filter_construct_too_many_spec() -> None: bin_spec = [ TwoPointTracerSpec(name="bin_1", measurement=Galaxies.COUNTS), TwoPointTracerSpec(name="bin_2", measurement=Galaxies.SHEAR_E), @@ -208,7 +212,7 @@ def test_two_point_bin_filter_construct_too_many_spec(): TwoPointBinFilter(spec=bin_spec, interval=(0.1, 0.5)) -def test_two_point_bin_filter_collection_construct(): +def test_two_point_bin_filter_collection_construct() -> None: bin_spec = ( TwoPointTracerSpec(name="bin_1", measurement=Galaxies.COUNTS), TwoPointTracerSpec(name="bin_2", measurement=Galaxies.SHEAR_E), @@ -656,7 +660,7 @@ def test_bin_filter_methods( assert bin_col.apply_filter_single(harmonic_window_bins[7]) -def test_raise_with_two_bins_same_name(): +def test_raise_with_two_bins_same_name() -> None: # Here we test if make_all_photoz_bin_combinations raises an error when # there are two bins with the same name and measurement. bin1 = TomographicBin( diff --git a/tests/metadata/test_metadata_two_point.py b/tests/metadata/test_metadata_two_point.py index de376f546..bc08e4958 100644 --- a/tests/metadata/test_metadata_two_point.py +++ b/tests/metadata/test_metadata_two_point.py @@ -5,6 +5,7 @@ import dataclasses import numpy as np +import numpy.typing as npt import pytest import sacc from numpy.testing import assert_array_equal @@ -47,7 +48,7 @@ ) -def test_inferred_galaxy_z_dist(): +def test_inferred_galaxy_z_dist() -> None: z_dist = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -62,7 +63,7 @@ def test_inferred_galaxy_z_dist(): assert z_dist.measurements == {Galaxies.COUNTS} -def test_inferred_galaxy_z_dist_bad_shape(): +def test_inferred_galaxy_z_dist_bad_shape() -> None: with pytest.raises( ValueError, match="The z and dndz arrays should have the same shape." ): @@ -74,7 +75,7 @@ def test_inferred_galaxy_z_dist_bad_shape(): ) -def test_inferred_galaxy_z_dist_bad_type(): +def test_inferred_galaxy_z_dist_bad_type() -> None: with pytest.raises( ValueError, match="The measurement should be a Galaxies Measurement." ): @@ -86,7 +87,7 @@ def test_inferred_galaxy_z_dist_bad_type(): ) -def test_inferred_galaxy_z_dist_bad_name(): +def test_inferred_galaxy_z_dist_bad_name() -> None: with pytest.raises(ValueError, match="The bin_name should not be empty."): TomographicBin( bin_name="", @@ -96,7 +97,7 @@ def test_inferred_galaxy_z_dist_bad_name(): ) -def test_two_point_xy_gal_gal(): +def test_two_point_xy_gal_gal() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -118,7 +119,7 @@ def test_two_point_xy_gal_gal(): assert xy.y_measurement == Galaxies.COUNTS -def test_two_point_xy_gal_gal_invalid_x_measurement(): +def test_two_point_xy_gal_gal_invalid_x_measurement() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -140,7 +141,7 @@ def test_two_point_xy_gal_gal_invalid_x_measurement(): ) -def test_two_point_xy_gal_gal_invalid_y_measurement(): +def test_two_point_xy_gal_gal_invalid_y_measurement() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -162,7 +163,7 @@ def test_two_point_xy_gal_gal_invalid_y_measurement(): ) -def test_two_point_xy_cmb_gal(): +def test_two_point_xy_cmb_gal() -> None: x = CMBLensing( bin_name="b_name1", z_lss=1100.0, @@ -183,7 +184,7 @@ def test_two_point_xy_cmb_gal(): assert xy.y_measurement == Galaxies.COUNTS -def test_two_point_xy_invalid(): +def test_two_point_xy_invalid() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -205,11 +206,11 @@ def test_two_point_xy_invalid(): ) -def test_two_point_xy_neq_other_type(harmonic_two_point_xy: TwoPointXY): +def test_two_point_xy_neq_other_type(harmonic_two_point_xy: TwoPointXY) -> None: assert harmonic_two_point_xy != object() -def test_two_point_harmonic(): +def test_two_point_harmonic() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -234,7 +235,7 @@ def test_two_point_harmonic(): assert cells.n_observations() == 100 -def test_two_point_harmonic_invalid_ells(): +def test_two_point_harmonic_invalid_ells() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -258,7 +259,7 @@ def test_two_point_harmonic_invalid_ells(): TwoPointHarmonic(ells=ells, XY=xy) -def test_two_point_harmonic_invalid_type(): +def test_two_point_harmonic_invalid_type() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -282,7 +283,7 @@ def test_two_point_harmonic_invalid_type(): TwoPointHarmonic(ells=ells, XY=xy) -def test_two_point_cwindow(harmonic_two_point_xy: TwoPointXY): +def test_two_point_cwindow(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) @@ -303,7 +304,7 @@ def test_two_point_cwindow(harmonic_two_point_xy: TwoPointXY): def test_two_point_cwindow_wrong_data_shape( harmonic_two_point_xy: TwoPointXY, -): +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) @@ -329,7 +330,7 @@ def test_two_point_cwindow_wrong_data_shape( ) -def test_two_point_measurement_invalid_metadata(): +def test_two_point_measurement_invalid_metadata() -> None: data = (np.zeros(100) + 1.1).astype(np.float64) indices = np.arange(100) covariance_name = "cov" @@ -345,7 +346,7 @@ def test_two_point_measurement_invalid_metadata(): ) -def test_two_point_cwindow_stringify(harmonic_two_point_xy: TwoPointXY): +def test_two_point_cwindow_stringify(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) @@ -359,7 +360,7 @@ def test_two_point_cwindow_stringify(harmonic_two_point_xy: TwoPointXY): ) -def test_two_point_cwindow_invalid(): +def test_two_point_cwindow_invalid() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) @@ -387,7 +388,7 @@ def test_two_point_cwindow_invalid(): TwoPointHarmonic(XY=xy, ells=ells, window=weights, window_ells=window_ells) -def test_two_point_cwindow_invalid_window(): +def test_two_point_cwindow_invalid_window() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -412,7 +413,7 @@ def test_two_point_cwindow_invalid_window(): ) -def test_two_point_cwindow_invalid_window_shape(): +def test_two_point_cwindow_invalid_window_shape() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400, dtype=np.float64) @@ -438,7 +439,7 @@ def test_two_point_cwindow_invalid_window_shape(): TwoPointHarmonic(XY=xy, ells=ells, window=weights) -def test_two_point_cwindow_window_ell_not_match(): +def test_two_point_cwindow_window_ell_not_match() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) @@ -464,7 +465,7 @@ def test_two_point_cwindow_window_ell_not_match(): TwoPointHarmonic(XY=xy, ells=ells[:10], window=weights) -def test_two_point_cwindow_missing_window_ells(): +def test_two_point_cwindow_missing_window_ells() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) @@ -490,7 +491,7 @@ def test_two_point_cwindow_missing_window_ells(): TwoPointHarmonic(XY=xy, ells=ells, window=weights) -def test_two_point_cwindow_window_ells_wrong_shape(): +def test_two_point_cwindow_window_ells_wrong_shape() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) @@ -522,7 +523,7 @@ def test_two_point_cwindow_window_ells_wrong_shape(): ) -def test_two_point_cwindow_window_ells_wrong_len(): +def test_two_point_cwindow_window_ells_wrong_len() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) @@ -554,7 +555,7 @@ def test_two_point_cwindow_window_ells_wrong_len(): ) -def test_two_point_cwindow_no_window_with_window_ells(): +def test_two_point_cwindow_no_window_with_window_ells() -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) x = TomographicBin( @@ -580,7 +581,7 @@ def test_two_point_cwindow_no_window_with_window_ells(): TwoPointHarmonic(XY=xy, ells=ells, window_ells=np.linspace(0, 1, 9)) -def test_two_point_real(): +def test_two_point_real() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -605,7 +606,7 @@ def test_two_point_real(): assert two_point.n_observations() == 100 -def test_two_point_real_invalid(): +def test_two_point_real_invalid() -> None: x = TomographicBin( bin_name="b_name1", z=np.linspace(0, 1, 100), @@ -629,35 +630,35 @@ def test_two_point_real_invalid(): TwoPointReal(XY=xy, thetas=theta) -def test_harmonic_type_string_invalid(): +def test_harmonic_type_string_invalid() -> None: with pytest.raises( ValueError, match="Harmonic-space correlation not supported for shear T." ): harmonic(Galaxies.SHEAR_T, Galaxies.COUNTS) -def test_real_type_string_invalid(): +def test_real_type_string_invalid() -> None: with pytest.raises( ValueError, match="Real-space correlation not supported for shear E." ): real(Galaxies.SHEAR_E, Galaxies.COUNTS) -def test_tracer_names_serialization(): +def test_tracer_names_serialization() -> None: tn = TracerNames("x", "y") s = tn.to_yaml() recovered = TracerNames.from_yaml(s) assert tn == recovered -def test_measurement_serialization(): +def test_measurement_serialization() -> None: for t in ALL_MEASUREMENTS: s = t.to_yaml() recovered = type(t).from_yaml(s) assert t == recovered -def test_inferred_galaxy_zdist_serialization(harmonic_bin_1: TomographicBin): +def test_inferred_galaxy_zdist_serialization(harmonic_bin_1: TomographicBin) -> None: s = harmonic_bin_1.to_yaml() # Take a look at how hideous the generated string # is. @@ -665,13 +666,13 @@ def test_inferred_galaxy_zdist_serialization(harmonic_bin_1: TomographicBin): assert harmonic_bin_1 == recovered -def test_two_point_xy_str(harmonic_two_point_xy: TwoPointXY): +def test_two_point_xy_str(harmonic_two_point_xy: TwoPointXY) -> None: assert str(harmonic_two_point_xy) == ( f"({harmonic_two_point_xy.x.bin_name}, " f"{harmonic_two_point_xy.y.bin_name})" ) -def test_two_point_xy_serialization(harmonic_two_point_xy: TwoPointXY): +def test_two_point_xy_serialization(harmonic_two_point_xy: TwoPointXY) -> None: s = harmonic_two_point_xy.to_yaml() # Take a look at how hideous the generated string # is. @@ -680,13 +681,13 @@ def test_two_point_xy_serialization(harmonic_two_point_xy: TwoPointXY): assert str(harmonic_two_point_xy) == str(recovered) -def test_two_point_harmonic_str(harmonic_two_point_xy: TwoPointXY): +def test_two_point_harmonic_str(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) cells = TwoPointHarmonic(ells=ells, XY=harmonic_two_point_xy) assert str(cells) == f"{str(harmonic_two_point_xy)}[{cells.get_sacc_name()}]" -def test_two_point_harmonic_serialization(harmonic_two_point_xy: TwoPointXY): +def test_two_point_harmonic_serialization(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) cells = TwoPointHarmonic(ells=ells, XY=harmonic_two_point_xy) s = cells.to_yaml() @@ -696,7 +697,7 @@ def test_two_point_harmonic_serialization(harmonic_two_point_xy: TwoPointXY): assert str(cells) == str(recovered) -def test_two_point_harmonic_cmp_invalid(harmonic_two_point_xy: TwoPointXY): +def test_two_point_harmonic_cmp_invalid(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) cells = TwoPointHarmonic(ells=ells, XY=harmonic_two_point_xy) with pytest.raises( @@ -706,7 +707,7 @@ def test_two_point_harmonic_cmp_invalid(harmonic_two_point_xy: TwoPointXY): _ = cells == "Im not a TwoPointHarmonic" -def test_two_point_harmonic_ells_wrong_shape(harmonic_two_point_xy: TwoPointXY): +def test_two_point_harmonic_ells_wrong_shape(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100), dtype=np.int64).reshape(-1, 10) with pytest.raises( ValueError, @@ -717,13 +718,13 @@ def test_two_point_harmonic_ells_wrong_shape(harmonic_two_point_xy: TwoPointXY): def test_two_point_harmonic_with_window_serialization( optimized_two_point_cwindow: TwoPointHarmonic, -): +) -> None: s = optimized_two_point_cwindow.to_yaml() recovered = TwoPointHarmonic.from_yaml(s) assert optimized_two_point_cwindow == recovered -def test_two_point_real_serialization(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_real_serialization(optimized_real_two_point_xy: TwoPointXY) -> None: theta = np.array(np.linspace(0, 10, 10)) xi_theta = TwoPointReal(XY=optimized_real_two_point_xy, thetas=theta) s = xi_theta.to_yaml() @@ -733,7 +734,7 @@ def test_two_point_real_serialization(optimized_real_two_point_xy: TwoPointXY): assert str(xi_theta) == str(recovered) -def test_two_point_real_cmp_invalid(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_real_cmp_invalid(optimized_real_two_point_xy: TwoPointXY) -> None: theta = np.array(np.linspace(0, 10, 10)) xi_theta = TwoPointReal(XY=optimized_real_two_point_xy, thetas=theta) with pytest.raises( @@ -743,7 +744,7 @@ def test_two_point_real_cmp_invalid(optimized_real_two_point_xy: TwoPointXY): _ = xi_theta == "Im not a TwoPointReal" -def test_two_point_real_wrong_shape(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_real_wrong_shape(optimized_real_two_point_xy: TwoPointXY) -> None: theta = np.array(np.linspace(0, 10), dtype=np.float64).reshape(-1, 10) with pytest.raises( ValueError, @@ -752,7 +753,9 @@ def test_two_point_real_wrong_shape(optimized_real_two_point_xy: TwoPointXY): TwoPointReal(XY=optimized_real_two_point_xy, thetas=theta) -def test_two_point_from_metadata_cells(harmonic_two_point_xy, tp_factory): +def test_two_point_from_metadata_cells( + harmonic_two_point_xy: TwoPointXY, tp_factory: TwoPointFactory +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) cells = TwoPointHarmonic(ells=ells, XY=harmonic_two_point_xy) two_point = TwoPoint.from_metadata([cells], tp_factory).pop() @@ -763,6 +766,8 @@ def test_two_point_from_metadata_cells(harmonic_two_point_xy, tp_factory): assert isinstance(two_point.source0, SourceGalaxy) assert isinstance(two_point.source1, SourceGalaxy) + assert isinstance(harmonic_two_point_xy.x, TomographicBin) + assert isinstance(harmonic_two_point_xy.y, TomographicBin) assert_array_equal(two_point.source0.tracer_args.z, harmonic_two_point_xy.x.z) assert_array_equal(two_point.source1.tracer_args.z, harmonic_two_point_xy.y.z) @@ -771,7 +776,9 @@ def test_two_point_from_metadata_cells(harmonic_two_point_xy, tp_factory): assert_array_equal(two_point.source1.tracer_args.dndz, harmonic_two_point_xy.y.dndz) -def test_two_point_from_metadata_cwindow(two_point_cwindow, tp_factory): +def test_two_point_from_metadata_cwindow( + two_point_cwindow: TwoPointHarmonic, tp_factory: TwoPointFactory +) -> None: two_point = TwoPoint.from_metadata([two_point_cwindow], tp_factory).pop() assert two_point is not None @@ -780,6 +787,8 @@ def test_two_point_from_metadata_cwindow(two_point_cwindow, tp_factory): assert isinstance(two_point.source0, SourceGalaxy) assert isinstance(two_point.source1, SourceGalaxy) + assert isinstance(two_point_cwindow.XY.x, TomographicBin) + assert isinstance(two_point_cwindow.XY.y, TomographicBin) assert_array_equal(two_point.source0.tracer_args.z, two_point_cwindow.XY.x.z) assert_array_equal(two_point.source1.tracer_args.z, two_point_cwindow.XY.y.z) @@ -788,7 +797,9 @@ def test_two_point_from_metadata_cwindow(two_point_cwindow, tp_factory): assert_array_equal(two_point.source1.tracer_args.dndz, two_point_cwindow.XY.y.dndz) -def test_two_point_from_metadata_xi_theta(optimized_real_two_point_xy, tp_factory): +def test_two_point_from_metadata_xi_theta( + optimized_real_two_point_xy: TwoPointXY, tp_factory: TwoPointFactory +) -> None: theta = np.array(np.linspace(0, 100, 100)) xi_theta = TwoPointReal(XY=optimized_real_two_point_xy, thetas=theta) if xi_theta.get_sacc_name() == "galaxy_shear_xi_tt": @@ -801,6 +812,8 @@ def test_two_point_from_metadata_xi_theta(optimized_real_two_point_xy, tp_factor assert isinstance(two_point.source0, SourceGalaxy) assert isinstance(two_point.source1, SourceGalaxy) + assert isinstance(optimized_real_two_point_xy.x, TomographicBin) + assert isinstance(optimized_real_two_point_xy.y, TomographicBin) assert_array_equal(two_point.source0.tracer_args.z, optimized_real_two_point_xy.x.z) assert_array_equal(two_point.source1.tracer_args.z, optimized_real_two_point_xy.y.z) @@ -813,7 +826,9 @@ def test_two_point_from_metadata_xi_theta(optimized_real_two_point_xy, tp_factor ) -def test_two_point_from_metadata_cells_unsupported_type(tp_factory): +def test_two_point_from_metadata_cells_unsupported_type( + tp_factory: TwoPointFactory, +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) @dataclasses.dataclass(frozen=True, kw_only=True) @@ -851,7 +866,7 @@ def measurement_list(self) -> list[Measurement]: @pytest.fixture(name="tp_factory_with_cmb") -def fixture_tp_factory_with_cmb(): +def fixture_tp_factory_with_cmb() -> TwoPointFactory: return TwoPointFactory( correlation_space=TwoPointCorrelationSpace.HARMONIC, weak_lensing_factories=[WeakLensingFactory()], @@ -860,7 +875,9 @@ def fixture_tp_factory_with_cmb(): ) -def test_two_point_from_metadata_cmb_supported(tp_factory_with_cmb): +def test_two_point_from_metadata_cmb_supported( + tp_factory_with_cmb: TwoPointFactory, +) -> None: """Test that CMB measurements work when CMB factory is provided.""" ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) x = CMBLensing( @@ -889,7 +906,7 @@ def test_two_point_from_metadata_cmb_supported(tp_factory_with_cmb): assert two_point.sacc_data_type == cells.get_sacc_name() -def test_make_two_point_xy_valid_galaxies(): +def test_make_two_point_xy_valid_galaxies() -> None: """Test make_two_point_xy with valid galaxy measurements.""" x = TomographicBin( bin_name="shear_bin_0", @@ -915,7 +932,7 @@ def test_make_two_point_xy_valid_galaxies(): assert xy.y_measurement == Galaxies.SHEAR_E -def test_make_two_point_xy_valid_cmb_galaxy(): +def test_make_two_point_xy_valid_cmb_galaxy() -> None: """Test make_two_point_xy with CMB-galaxy measurements.""" cmb = CMBLensing( bin_name="cmb_convergence", @@ -943,7 +960,7 @@ def test_make_two_point_xy_valid_cmb_galaxy(): assert xy.y_measurement == Galaxies.COUNTS -def test_make_two_point_xy_valid_cmb_galaxy_needs_swap(): +def test_make_two_point_xy_valid_cmb_galaxy_needs_swap() -> None: """Test make_two_point_xy with CMB-galaxy measurements.""" cmb = CMBLensing( bin_name="cmb_convergence", @@ -973,7 +990,7 @@ def test_make_two_point_xy_valid_cmb_galaxy_needs_swap(): assert xy.y_measurement == Galaxies.COUNTS -def test_make_two_point_xy_missing_tracer_zdist(): +def test_make_two_point_xy_missing_tracer_zdist() -> None: """Test make_two_point_xy raises exception when tracer zdist not found. Verifies that make_two_point_xy raises a ValueError with informative message @@ -1000,7 +1017,7 @@ def test_make_two_point_xy_missing_tracer_zdist(): assert "not found in inferred galaxy z distributions" in error_msg -def test_make_two_point_xy_missing_x_measurement(): +def test_make_two_point_xy_missing_x_measurement() -> None: """Test make_two_point_xy when first tracer lacks required measurement.""" x = TomographicBin( bin_name="shear_bin_0", @@ -1031,7 +1048,7 @@ def test_make_two_point_xy_missing_x_measurement(): assert "shear_bin_1" in error_msg -def test_make_two_point_xy_missing_y_measurement(): +def test_make_two_point_xy_missing_y_measurement() -> None: """Test make_two_point_xy when second tracer lacks required measurement.""" x = TomographicBin( bin_name="shear_bin_0", @@ -1061,7 +1078,7 @@ def test_make_two_point_xy_missing_y_measurement(): assert "shear_bin_1" in error_msg -def test_make_two_point_xy_both_measurements_missing(): +def test_make_two_point_xy_both_measurements_missing() -> None: """Test make_two_point_xy when both tracers lack required measurements.""" x = TomographicBin( bin_name="counts_bin_0", @@ -1089,7 +1106,7 @@ def test_make_two_point_xy_both_measurements_missing(): ) -def test_make_two_point_xy_sacc_convention_explanation(): +def test_make_two_point_xy_sacc_convention_explanation() -> None: """Test that error message includes SACC convention explanation.""" x = CMBLensing( bin_name="cmb_bin", @@ -1118,8 +1135,13 @@ def test_make_two_point_xy_sacc_convention_explanation(): def test_extract_all_real_metadata_indices_no_swap( - sacc_galaxy_xis_src0_lens0: tuple[object, ...], -): + sacc_galaxy_xis_src0_lens0: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: """Test extract_all_real_metadata_indices with no tracer swap needed. This test verifies that when tracers are in the correct order according to @@ -1147,7 +1169,7 @@ def test_extract_all_real_metadata_indices_no_swap( assert b == Galaxies.COUNTS -def test_extract_all_real_metadata_indices_with_swap(): +def test_extract_all_real_metadata_indices_with_swap() -> None: """Test extract_all_real_metadata_indices when tracer swap is necessary. This test creates a SACC file where tracers are in reversed order compared @@ -1205,7 +1227,7 @@ def test_extract_all_real_metadata_indices_with_swap(): assert b == Galaxies.SHEAR_T -def test_extract_all_real_metadata_indices_density_only(): +def test_extract_all_real_metadata_indices_density_only() -> None: """Test extract_all_real_metadata_indices with density-only measurement.""" sacc_data = sacc.Sacc() @@ -1238,7 +1260,7 @@ def test_extract_all_real_metadata_indices_density_only(): assert b == Galaxies.COUNTS -def test_extract_all_real_metadata_indices_shear_only(): +def test_extract_all_real_metadata_indices_shear_only() -> None: """Test extract_all_real_metadata_indices with shear-density measurement.""" sacc_data = sacc.Sacc() @@ -1271,7 +1293,7 @@ def test_extract_all_real_metadata_indices_shear_only(): assert b == Galaxies.COUNTS -def test_extract_all_real_metadata_indices_multiple_combinations(): +def test_extract_all_real_metadata_indices_multiple_combinations() -> None: """Test extract_all_real_metadata_indices with multiple tracer combinations.""" sacc_data = sacc.Sacc() @@ -1318,8 +1340,13 @@ def test_extract_all_real_metadata_indices_multiple_combinations(): def test_extract_all_harmonic_metadata_indices_no_swap( - sacc_galaxy_cells_src0_lens0: tuple[object, ...], -): + sacc_galaxy_cells_src0_lens0: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: """Test extract_all_harmonic_metadata_indices with no tracer swap needed. This test verifies that when tracers are in the correct order according to @@ -1346,7 +1373,7 @@ def test_extract_all_harmonic_metadata_indices_no_swap( assert b == Galaxies.COUNTS -def test_extract_all_harmonic_metadata_indices_with_swap(): +def test_extract_all_harmonic_metadata_indices_with_swap() -> None: """Test extract_all_harmonic_metadata_indices when tracer swap is necessary. This test creates a SACC file where tracers are in reversed order compared @@ -1402,7 +1429,7 @@ def test_extract_all_harmonic_metadata_indices_with_swap(): assert b == Galaxies.SHEAR_E -def test_extract_all_harmonic_metadata_indices_density_only(): +def test_extract_all_harmonic_metadata_indices_density_only() -> None: """Test extract_all_harmonic_metadata_indices with density-only measurement.""" sacc_data = sacc.Sacc() @@ -1435,7 +1462,7 @@ def test_extract_all_harmonic_metadata_indices_density_only(): assert b == Galaxies.COUNTS -def test_extract_all_harmonic_metadata_indices_shear_only(): +def test_extract_all_harmonic_metadata_indices_shear_only() -> None: """Test extract_all_harmonic_metadata_indices with shear-only measurement.""" sacc_data = sacc.Sacc() @@ -1468,7 +1495,7 @@ def test_extract_all_harmonic_metadata_indices_shear_only(): assert b == Galaxies.SHEAR_E -def test_extract_all_harmonic_metadata_indices_multiple_combinations(): +def test_extract_all_harmonic_metadata_indices_multiple_combinations() -> None: """Test extract_all_harmonic_metadata_indices with multiple tracer combinations.""" sacc_data = sacc.Sacc() diff --git a/tests/metadata/test_metadata_two_point_measurement.py b/tests/metadata/test_metadata_two_point_measurement.py index e3aee98ca..e6a854ec6 100644 --- a/tests/metadata/test_metadata_two_point_measurement.py +++ b/tests/metadata/test_metadata_two_point_measurement.py @@ -20,7 +20,7 @@ ) -def test_two_point_cells_with_data(harmonic_two_point_xy: TwoPointXY): +def test_two_point_cells_with_data(harmonic_two_point_xy: TwoPointXY) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) data = np.array(np.ones_like(ells) * 1.1, dtype=np.float64) indices = np.arange(100) @@ -42,7 +42,9 @@ def test_two_point_cells_with_data(harmonic_two_point_xy: TwoPointXY): assert tpm.covariance_name == covariance_name -def test_two_point_two_point_cwindow_with_data(harmonic_two_point_xy: TwoPointXY): +def test_two_point_two_point_cwindow_with_data( + harmonic_two_point_xy: TwoPointXY, +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.zeros((100, 4), dtype=np.float64) # Create a window with 4 bins, each containing 25 elements with a weight of 1.0. @@ -83,7 +85,7 @@ def test_two_point_two_point_cwindow_with_data(harmonic_two_point_xy: TwoPointXY assert tpm.covariance_name == covariance_name -def test_two_point_xi_theta_with_data(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_xi_theta_with_data(optimized_real_two_point_xy: TwoPointXY) -> None: thetas = np.linspace(0.0, 1.0, 100, dtype=np.float64) data = np.array(np.zeros(100) + 1.1, dtype=np.float64) indices = np.arange(100) @@ -107,7 +109,9 @@ def test_two_point_xi_theta_with_data(optimized_real_two_point_xy: TwoPointXY): assert tpm.covariance_name == covariance_name -def test_two_point_cells_with_invalid_data_size(harmonic_two_point_xy: TwoPointXY): +def test_two_point_cells_with_invalid_data_size( + harmonic_two_point_xy: TwoPointXY, +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) data = np.array(np.zeros(101) + 1.1, dtype=np.float64) indices = np.arange(101) @@ -125,7 +129,9 @@ def test_two_point_cells_with_invalid_data_size(harmonic_two_point_xy: TwoPointX ) -def test_two_point_cwindow_with_invalid_data_size(harmonic_two_point_xy: TwoPointXY): +def test_two_point_cwindow_with_invalid_data_size( + harmonic_two_point_xy: TwoPointXY, +) -> None: ells = np.array(np.linspace(0, 100, 100), dtype=np.int64) weights = np.ones(400).reshape(-1, 4) window_ells = np.array([0, 1, 2, 3], dtype=np.float64) @@ -154,7 +160,7 @@ def test_two_point_cwindow_with_invalid_data_size(harmonic_two_point_xy: TwoPoin def test_two_point_xi_theta_with_invalid_data_size( optimized_real_two_point_xy: TwoPointXY, -): +) -> None: thetas = np.linspace(0.0, 1.0, 100, dtype=np.float64) data = np.array(np.zeros(101) + 1.1, dtype=np.float64) indices = np.arange(101) @@ -172,7 +178,9 @@ def test_two_point_xi_theta_with_invalid_data_size( ) -def test_two_point_measurement_invalid_data(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_measurement_invalid_data( + optimized_real_two_point_xy: TwoPointXY, +) -> None: thetas = np.linspace(0.0, 1.0, 5, dtype=np.float64) data = np.array([1, 2, 3, 4, 5], dtype=np.float64).reshape(-1, 1) indices = np.array([1, 2, 3, 4, 5]) @@ -189,7 +197,9 @@ def test_two_point_measurement_invalid_data(optimized_real_two_point_xy: TwoPoin ) -def test_two_point_measurement_invalid_indices(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_measurement_invalid_indices( + optimized_real_two_point_xy: TwoPointXY, +) -> None: thetas = np.linspace(0.0, 1.0, 5, dtype=np.float64) data = np.array([1, 2, 3, 4, 5]) indices = np.array([1, 2, 3, 4, 5]).reshape(-1, 1) @@ -206,7 +216,7 @@ def test_two_point_measurement_invalid_indices(optimized_real_two_point_xy: TwoP ) -def test_two_point_measurement_eq(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_measurement_eq(optimized_real_two_point_xy: TwoPointXY) -> None: thetas = np.linspace(0.0, 1.0, 5, dtype=np.float64) data = np.array([1, 2, 3, 4, 5]) indices = np.array([1, 2, 3, 4, 5]) @@ -226,7 +236,7 @@ def test_two_point_measurement_eq(optimized_real_two_point_xy: TwoPointXY): assert measure_1 == measure_2 -def test_two_point_measurement_neq(optimized_real_two_point_xy: TwoPointXY): +def test_two_point_measurement_neq(optimized_real_two_point_xy: TwoPointXY) -> None: thetas = np.linspace(0.0, 1.0, 5, dtype=np.float64) data = np.array([1.0, 2.0, 3.0, 4.0, 5.0], dtype=np.float64) indices = np.array([1, 2, 3, 4, 5]) @@ -274,7 +284,7 @@ def test_two_point_measurement_neq(optimized_real_two_point_xy: TwoPointXY): def test_two_point_measurement_neq_other_type( optimized_real_two_point_xy: TwoPointXY, -): +) -> None: thetas = np.linspace(0.0, 1.0, 5, dtype=np.float64) measurement = TwoPointMeasurement( data=np.array([1.0, 2.0, 3.0, 4.0, 5.0]), diff --git a/tests/metadata/test_metadata_two_point_sacc.py b/tests/metadata/test_metadata_two_point_sacc.py index 7613c484e..af69a5412 100644 --- a/tests/metadata/test_metadata_two_point_sacc.py +++ b/tests/metadata/test_metadata_two_point_sacc.py @@ -23,6 +23,7 @@ TwoPointFactory, use_source_factory, ) +from firecrown.modeling_tools import ModelingTools from firecrown.metadata_functions import ( extract_all_harmonic_metadata, extract_all_harmonic_metadata_indices, @@ -57,13 +58,14 @@ _type_to_sacc_string_real, ) from firecrown.updatable import ParamsMap +from tests._types import SaccGalaxyWindowedFixture # pylint: disable=too-many-lines @pytest.fixture(name="sacc_galaxy_src0_src0_invalid_data_type") def fixture_sacc_galaxy_src0_src0_invalid_data_type( - recwarn, + recwarn: pytest.WarningsRecorder, ) -> tuple[sacc.Sacc, np.ndarray, np.ndarray]: """Fixture for a SACC data without window functions.""" sacc_data = sacc.Sacc() @@ -155,7 +157,16 @@ def fixture_sacc_galaxy_cells_three_tracers() -> ( return sacc_data, z, dndz0, dndz1, dndz2 -def test_extract_all_tracers_cells(sacc_galaxy_cells) -> None: +def test_extract_all_tracers_cells( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, tracers, _ = sacc_galaxy_cells assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -166,7 +177,16 @@ def test_extract_all_tracers_cells(sacc_galaxy_cells) -> None: assert_array_equal(tracer.dndz, orig_tracer[1]) -def test_extract_all_tracers_xis(sacc_galaxy_xis): +def test_extract_all_tracers_xis( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, tracers, _ = sacc_galaxy_xis assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -177,21 +197,30 @@ def test_extract_all_tracers_xis(sacc_galaxy_xis): assert_array_equal(tracer.dndz, orig_tracer[1]) -def test_extract_all_tracers_cells_src0_src0(sacc_galaxy_cells_src0_src0): - sacc_data, z, dndz = sacc_galaxy_cells_src0_src0 +def test_extract_all_tracers_cells_src0_src0( + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, expected_bin = sacc_galaxy_cells_src0_src0 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) assert len(all_tracers) == 1 for tracer in all_tracers: - assert_array_equal(tracer.z, z) - assert_array_equal(tracer.dndz, dndz) + assert_array_equal(tracer.z, expected_bin.z) + assert_array_equal(tracer.dndz, expected_bin.dndz) assert tracer.bin_name == "src0" assert tracer.measurements == {Galaxies.SHEAR_E} -def test_extract_all_tracers_cells_src0_src1(sacc_galaxy_cells_src0_src1): +def test_extract_all_tracers_cells_src0_src1( + sacc_galaxy_cells_src0_src1: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: sacc_data, z, dndz0, dndz1 = sacc_galaxy_cells_src0_src1 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -207,21 +236,30 @@ def test_extract_all_tracers_cells_src0_src1(sacc_galaxy_cells_src0_src1): assert_array_equal(tracer.dndz, dndz1) -def test_extract_all_tracers_cells_lens0_lens0(sacc_galaxy_cells_lens0_lens0): - sacc_data, z, dndz = sacc_galaxy_cells_lens0_lens0 +def test_extract_all_tracers_cells_lens0_lens0( + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, expected_bin = sacc_galaxy_cells_lens0_lens0 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) assert len(all_tracers) == 1 for tracer in all_tracers: - assert_array_equal(tracer.z, z) - assert_array_equal(tracer.dndz, dndz) + assert_array_equal(tracer.z, expected_bin.z) + assert_array_equal(tracer.dndz, expected_bin.dndz) assert tracer.bin_name == "lens0" assert tracer.measurements == {Galaxies.COUNTS} -def test_extract_all_tracers_cells_lens0_lens1(sacc_galaxy_cells_lens0_lens1): +def test_extract_all_tracers_cells_lens0_lens1( + sacc_galaxy_cells_lens0_lens1: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: sacc_data, z, dndz0, dndz1 = sacc_galaxy_cells_lens0_lens1 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -237,21 +275,30 @@ def test_extract_all_tracers_cells_lens0_lens1(sacc_galaxy_cells_lens0_lens1): assert_array_equal(tracer.dndz, dndz1) -def test_extract_all_tracers_xis_lens0_lens0(sacc_galaxy_xis_lens0_lens0): - sacc_data, z, dndz = sacc_galaxy_xis_lens0_lens0 +def test_extract_all_tracers_xis_lens0_lens0( + sacc_galaxy_xis_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: + sacc_data, expected_bin = sacc_galaxy_xis_lens0_lens0 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) assert len(all_tracers) == 1 for tracer in all_tracers: - assert_array_equal(tracer.z, z) - assert_array_equal(tracer.dndz, dndz) + assert_array_equal(tracer.z, expected_bin.z) + assert_array_equal(tracer.dndz, expected_bin.dndz) assert tracer.bin_name == "lens0" assert tracer.measurements == {Galaxies.COUNTS} -def test_extract_all_tracers_xis_lens0_lens1(sacc_galaxy_xis_lens0_lens1): +def test_extract_all_tracers_xis_lens0_lens1( + sacc_galaxy_xis_lens0_lens1: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: sacc_data, z, dndz0, dndz1 = sacc_galaxy_xis_lens0_lens1 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -267,7 +314,14 @@ def test_extract_all_tracers_xis_lens0_lens1(sacc_galaxy_xis_lens0_lens1): assert_array_equal(tracer.dndz, dndz1) -def test_extract_all_trace_cells_src0_lens0(sacc_galaxy_cells_src0_lens0): +def test_extract_all_trace_cells_src0_lens0( + sacc_galaxy_cells_src0_lens0: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: sacc_data, z, dndz0, dndz1 = sacc_galaxy_cells_src0_lens0 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -285,7 +339,14 @@ def test_extract_all_trace_cells_src0_lens0(sacc_galaxy_cells_src0_lens0): assert tracer.measurements == {Galaxies.COUNTS} -def test_extract_all_trace_xis_src0_lens0(sacc_galaxy_xis_src0_lens0): +def test_extract_all_trace_xis_src0_lens0( + sacc_galaxy_xis_src0_lens0: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: sacc_data, z, dndz0, dndz1 = sacc_galaxy_xis_src0_lens0 assert sacc_data is not None all_tracers = extract_all_tracers_tomographic_bins(sacc_data) @@ -304,8 +365,8 @@ def test_extract_all_trace_xis_src0_lens0(sacc_galaxy_xis_src0_lens0): def test_extract_all_tracers_invalid_data_type( - sacc_galaxy_src0_src0_invalid_data_type, -): + sacc_galaxy_src0_src0_invalid_data_type: tuple[sacc.Sacc, np.ndarray, np.ndarray], +) -> None: sacc_data, _, _ = sacc_galaxy_src0_src0_invalid_data_type assert sacc_data is not None with pytest.raises( @@ -493,7 +554,16 @@ def test_extract_all_tracers_projected_fields_map_tracer_without_data() -> None: _ = extract_all_tracers_projected_fields(sacc_data) -def test_extract_all_metadata_index_harmonics(sacc_galaxy_cells): +def test_extract_all_metadata_index_harmonics( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cells all_data = extract_all_harmonic_metadata_indices(sacc_data) @@ -504,7 +574,16 @@ def test_extract_all_metadata_index_harmonics(sacc_galaxy_cells): assert (tracer_names, two_point["data_type"]) in tracer_pairs -def test_extract_all_metadata_index_harmonics_by_type(sacc_galaxy_cells): +def test_extract_all_metadata_index_harmonics_by_type( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cells all_data = extract_all_harmonic_metadata_indices( @@ -517,7 +596,16 @@ def test_extract_all_metadata_index_harmonics_by_type(sacc_galaxy_cells): assert (tracer_names, "galaxy_shear_cl_ee") in tracer_pairs -def test_extract_all_metadata_index_reals(sacc_galaxy_xis): +def test_extract_all_metadata_index_reals( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_xis all_data = extract_all_real_metadata_indices(sacc_data) @@ -551,11 +639,9 @@ def test_extract_all_metadata_index_reals_by_type( def test_extract_no_window( - sacc_galaxy_cells_src0_src0: tuple[ - sacc.Sacc, npt.NDArray[np.float64], npt.NDArray[np.float64] - ], + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], ) -> None: - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 + sacc_data, _ = sacc_galaxy_cells_src0_src0 indices = np.array([0, 1, 2], dtype=np.int64) with warnings.catch_warnings(): @@ -570,10 +656,10 @@ def test_extract_no_window( def test_extract_all_data_types_three_tracers_reals( sacc_galaxy_xis_three_tracers: tuple[ sacc.Sacc, - dict[str, object], - dict[str, object], - dict[str, object], - dict[str, object], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], ], ) -> None: sacc_data, _, _, _, _ = sacc_galaxy_xis_three_tracers @@ -591,10 +677,10 @@ def test_extract_all_data_types_three_tracers_reals( def test_extract_all_data_types_three_tracers_harmonics( sacc_galaxy_cells_three_tracers: tuple[ sacc.Sacc, - dict[str, object], - dict[str, object], - dict[str, object], - dict[str, object], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], ], ) -> None: sacc_data, _, _, _, _ = sacc_galaxy_cells_three_tracers @@ -758,14 +844,7 @@ def test_extract_all_harmonic_data( @pytest.mark.slow def test_extract_all_harmonic_with_window_data( - sacc_galaxy_cwindows: tuple[ - sacc.Sacc, - dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], - dict[ - tuple[TracerNames, str], - tuple[npt.NDArray[np.int64], npt.NDArray[np.float64], sacc.BandpowerWindow], - ], - ], + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, ) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cwindows @@ -799,7 +878,7 @@ def test_extract_all_real_data( tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], ], ], -): +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_xis two_point_xis = extract_all_real_data(sacc_data) @@ -828,7 +907,7 @@ def test_constructor_harmonic_metadata( ], ], tp_factory: TwoPointFactory, -): +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cells two_point_harmonics = extract_all_harmonic_metadata(sacc_data) @@ -845,7 +924,17 @@ def test_constructor_harmonic_metadata( @pytest.mark.slow -def test_constructor_harmonic_data(sacc_galaxy_cells, tp_factory): +def test_constructor_harmonic_data( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cells two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -865,7 +954,10 @@ def test_constructor_harmonic_data(sacc_galaxy_cells, tp_factory): ) -def test_constructor_harmonic_with_window_metadata(sacc_galaxy_cwindows, tp_factory): +def test_constructor_harmonic_with_window_metadata( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_metadata(sacc_data) @@ -876,9 +968,8 @@ def test_constructor_harmonic_with_window_metadata(sacc_galaxy_cwindows, tp_fact for two_point in two_points_new: tracer_pairs_key = (two_point.sacc_tracers, two_point.sacc_data_type) - _, _, window = tracer_pairs[tracer_pairs_key] - assert tracer_pairs_key in tracer_pairs + _, _, window = tracer_pairs[tracer_pairs_key] assert two_point.ells is not None assert two_point.window is not None assert_array_equal( @@ -888,7 +979,9 @@ def test_constructor_harmonic_with_window_metadata(sacc_galaxy_cwindows, tp_fact assert_array_equal(two_point.ells, window.values) -def test_extract_all_harmonic_metadata_normalize_true(sacc_galaxy_cwindows): +def test_extract_all_harmonic_metadata_normalize_true( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, +) -> None: """Test that normalize=True normalizes windows in extract_all_harmonic_metadata.""" sacc_data, _, _ = sacc_galaxy_cwindows @@ -903,7 +996,9 @@ def test_extract_all_harmonic_metadata_normalize_true(sacc_galaxy_cwindows): assert_allclose(window_sum, np.ones_like(window_sum)) -def test_extract_all_harmonic_metadata_normalize_false(sacc_galaxy_cwindows): +def test_extract_all_harmonic_metadata_normalize_false( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, +) -> None: """Test that normalize=False keeps windows unnormalized.""" sacc_data, _, _ = sacc_galaxy_cwindows @@ -922,7 +1017,9 @@ def test_extract_all_harmonic_metadata_normalize_false(sacc_galaxy_cwindows): assert not np.allclose(window_sum, np.ones_like(window_sum)) -def test_extract_all_harmonic_metadata_default_normalizes(sacc_galaxy_cwindows): +def test_extract_all_harmonic_metadata_default_normalizes( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, +) -> None: """Test that the default behavior (no normalize parameter) normalizes windows.""" sacc_data, _, _ = sacc_galaxy_cwindows @@ -937,7 +1034,7 @@ def test_extract_all_harmonic_metadata_default_normalizes(sacc_galaxy_cwindows): assert_allclose(window_sum, np.ones_like(window_sum)) -def test_extract_window_function_raises_on_zero_weight(): +def test_extract_window_function_raises_on_zero_weight() -> None: """Test that extract_window_function raises ValueError for zero-weight columns.""" sacc_data = sacc.Sacc() @@ -983,7 +1080,7 @@ def test_extract_window_function_raises_on_zero_weight(): extract_window_function(sacc_data, indices, normalize=True) -def test_extract_window_function_raises_on_multiple_zero_weights(): +def test_extract_window_function_raises_on_multiple_zero_weights() -> None: """Test that extract_window_function reports multiple zero-weight columns.""" sacc_data = sacc.Sacc() @@ -1029,7 +1126,7 @@ def test_extract_window_function_raises_on_multiple_zero_weights(): extract_window_function(sacc_data, indices, normalize=False) -def test_extract_all_harmonic_metadata_raises_on_zero_weight(): +def test_extract_all_harmonic_metadata_raises_on_zero_weight() -> None: """Test that extract_all_harmonic_metadata raises ValueError for zero weights.""" sacc_data = sacc.Sacc() @@ -1069,7 +1166,10 @@ def test_extract_all_harmonic_metadata_raises_on_zero_weight(): @pytest.mark.slow -def test_constructor_harmonic_with_window_data(sacc_galaxy_cwindows, tp_factory): +def test_constructor_harmonic_with_window_data( + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -1081,9 +1181,8 @@ def test_constructor_harmonic_with_window_data(sacc_galaxy_cwindows, tp_factory) for two_point in two_points_new: tracer_pairs_key = (two_point.sacc_tracers, two_point.sacc_data_type) - _, Cell, window = tracer_pairs[tracer_pairs_key] - assert tracer_pairs_key in tracer_pairs + _, Cell, window = tracer_pairs[tracer_pairs_key] assert two_point.ells is not None assert_array_equal(two_point.get_data_vector(), Cell) assert two_point.window is not None @@ -1094,7 +1193,17 @@ def test_constructor_harmonic_with_window_data(sacc_galaxy_cwindows, tp_factory) assert_array_equal(two_point.ells, window.values) -def test_constructor_reals_metadata(sacc_galaxy_xis, tp_factory): +def test_constructor_reals_metadata( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_xis two_point_reals = extract_all_real_metadata(sacc_data) @@ -1111,7 +1220,17 @@ def test_constructor_reals_metadata(sacc_galaxy_xis, tp_factory): @pytest.mark.slow -def test_constructor_reals_data(sacc_galaxy_xis, tp_factory): +def test_constructor_reals_data( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], + tp_factory: TwoPointFactory, +) -> None: sacc_data, _, tracer_pairs = sacc_galaxy_xis two_point_reals = extract_all_real_data(sacc_data) @@ -1132,8 +1251,17 @@ def test_constructor_reals_data(sacc_galaxy_xis, tp_factory): def test_compare_constructors_harmonic( - sacc_galaxy_cells, tp_factory, tools_with_vanilla_cosmology -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], + tp_factory: TwoPointFactory, + tools_with_vanilla_cosmology: ModelingTools, +) -> None: sacc_data, _, _ = sacc_galaxy_cells two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -1178,8 +1306,10 @@ def test_compare_constructors_harmonic( def test_compare_constructors_harmonic_with_window( - sacc_galaxy_cwindows, tp_factory, tools_with_vanilla_cosmology -): + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, + tp_factory: TwoPointFactory, + tools_with_vanilla_cosmology: ModelingTools, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows two_point_harmonics = extract_all_harmonic_data(sacc_data) @@ -1230,8 +1360,17 @@ def test_compare_constructors_harmonic_with_window( def test_compare_constructors_reals( - sacc_galaxy_xis, tp_factory, tools_with_vanilla_cosmology -): + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], + tp_factory: TwoPointFactory, + tools_with_vanilla_cosmology: ModelingTools, +) -> None: sacc_data, _, _ = sacc_galaxy_xis two_point_xis = extract_all_real_data(sacc_data) @@ -1275,7 +1414,16 @@ def test_compare_constructors_reals( ) -def test_extract_all_data_harmonic_no_cov(sacc_galaxy_cells): +def test_extract_all_data_harmonic_no_cov( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_cells sacc_data.covariance = None with pytest.raises( @@ -1285,7 +1433,16 @@ def test_extract_all_data_harmonic_no_cov(sacc_galaxy_cells): _ = extract_all_harmonic_data(sacc_data) -def test_extract_all_data_real_no_cov(sacc_galaxy_xis): +def test_extract_all_data_real_no_cov( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_xis sacc_data.covariance = None with pytest.raises( @@ -1295,7 +1452,7 @@ def test_extract_all_data_real_no_cov(sacc_galaxy_xis): _ = extract_all_real_data(sacc_data) -def test_make_all_photoz_bin_combinations_with_cmb_basic(): +def test_make_all_photoz_bin_combinations_with_cmb_basic() -> None: """Test basic functionality of make_all_photoz_bin_combinations_with_cmb.""" # Create test galaxy bins galaxy_bins = [ @@ -1338,7 +1495,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_basic(): assert len(galaxy_combinations_in_result) == len(galaxy_only_combinations) -def test_make_all_photoz_bin_combinations_with_cmb_with_auto(): +def test_make_all_photoz_bin_combinations_with_cmb_with_auto() -> None: """Test make_all_photoz_bin_combinations_with_cmb with CMB auto-correlation.""" galaxy_bins = [ TomographicBin( @@ -1372,7 +1529,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_with_auto(): assert cmb_auto.y.bin_name == "cmb_convergence" -def test_make_all_photoz_bin_combinations_with_cmb_custom_tracer_name(): +def test_make_all_photoz_bin_combinations_with_cmb_custom_tracer_name() -> None: """Test make_all_photoz_bin_combinations_with_cmb with custom CMB tracer name.""" galaxy_bins = [ TomographicBin( @@ -1406,7 +1563,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_custom_tracer_name(): assert combo.y.bin_name == custom_name -def test_make_all_photoz_bin_combinations_with_cmb_measurement_compatibility(): +def test_make_all_photoz_bin_combinations_with_cmb_measurement_compatibility() -> None: """Test that only compatible measurements create CMB-galaxy cross-correlations.""" galaxy_bins = [ TomographicBin( @@ -1457,7 +1614,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_measurement_compatibility(): assert expected in actual_combinations -def test_make_all_photoz_bin_combinations_with_cmb_empty_input(): +def test_make_all_photoz_bin_combinations_with_cmb_empty_input() -> None: """Test make_all_photoz_bin_combinations_with_cmb with empty galaxy bins.""" combinations = make_all_photoz_bin_combinations_with_cmb([]) @@ -1465,7 +1622,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_empty_input(): assert len(combinations) == 0 -def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties(): +def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties() -> None: """Test that the created CMB bin has correct properties.""" galaxy_bins = [ TomographicBin( @@ -1491,7 +1648,9 @@ def test_make_all_photoz_bin_combinations_with_cmb_cmb_bin_properties(): @pytest.mark.parametrize("include_auto", [True, False]) -def test_make_all_photoz_bin_combinations_with_cmb_parametrized(include_auto: bool): +def test_make_all_photoz_bin_combinations_with_cmb_parametrized( + include_auto: bool, +) -> None: """Parametrized test for CMB auto-correlation inclusion.""" galaxy_bins = [ TomographicBin( @@ -1522,7 +1681,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_parametrized(include_auto: bo assert cmb_auto_count == 0 -def test_make_all_photoz_bin_combinations_with_cmb_multiple_measurements(): +def test_make_all_photoz_bin_combinations_with_cmb_multiple_measurements() -> None: """Test with galaxy bins that have multiple measurement types.""" galaxy_bins = [ TomographicBin( @@ -1566,7 +1725,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_multiple_measurements(): assert Galaxies.SHEAR_E in galaxy_measurements -def test_make_cmb_galaxy_combinations_only_basic(): +def test_make_cmb_galaxy_combinations_only_basic() -> None: """Test basic functionality of make_cmb_galaxy_combinations_only.""" # Create test galaxy bins galaxy_bins = [ @@ -1621,7 +1780,7 @@ def test_make_cmb_galaxy_combinations_only_basic(): assert len(cmb_auto_combinations) == 0 -def test_make_cmb_galaxy_combinations_only_custom_tracer_name(): +def test_make_cmb_galaxy_combinations_only_custom_tracer_name() -> None: """Test make_cmb_galaxy_combinations_only with custom CMB tracer name.""" galaxy_bins = [ TomographicBin( @@ -1655,7 +1814,7 @@ def test_make_cmb_galaxy_combinations_only_custom_tracer_name(): assert combo.y.bin_name == custom_name -def test_make_cmb_galaxy_combinations_only_measurement_compatibility(): +def test_make_cmb_galaxy_combinations_only_measurement_compatibility() -> None: """Test that only compatible measurements create CMB-galaxy cross-correlations.""" galaxy_bins = [ TomographicBin( @@ -1692,7 +1851,7 @@ def test_make_cmb_galaxy_combinations_only_measurement_compatibility(): assert expected in actual_combinations -def test_make_cmb_galaxy_combinations_only_empty_input(): +def test_make_cmb_galaxy_combinations_only_empty_input() -> None: """Test make_cmb_galaxy_combinations_only with empty galaxy bins.""" combinations = make_cmb_galaxy_combinations_only([]) @@ -1700,7 +1859,7 @@ def test_make_cmb_galaxy_combinations_only_empty_input(): assert len(combinations) == 0 -def test_make_cmb_galaxy_combinations_only_incompatible_measurements_0(): +def test_make_cmb_galaxy_combinations_only_incompatible_measurements_0() -> None: """Test behavior with galaxy measurements incompatible with CMB convergence.""" # Create a galaxy bin with a measurement that might not be compatible # (This test depends on what measurements are actually incompatible) @@ -1726,7 +1885,7 @@ def test_make_cmb_galaxy_combinations_only_incompatible_measurements_0(): ) -def test_make_cmb_galaxy_combinations_only_cmb_bin_properties(): +def test_make_cmb_galaxy_combinations_only_cmb_bin_properties() -> None: """Test that the created CMB bin has correct properties.""" galaxy_bins = [ TomographicBin( @@ -1751,7 +1910,7 @@ def test_make_cmb_galaxy_combinations_only_cmb_bin_properties(): assert cmb_bin.type_source == TypeSource.DEFAULT -def test_make_cmb_galaxy_combinations_only_multiple_measurements(): +def test_make_cmb_galaxy_combinations_only_multiple_measurements() -> None: """Test with galaxy bins that have multiple measurement types.""" galaxy_bins = [ TomographicBin( @@ -1782,7 +1941,7 @@ def test_make_cmb_galaxy_combinations_only_multiple_measurements(): assert Galaxies.SHEAR_E in galaxy_measurements -def test_make_cmb_galaxy_combinations_only_symmetric_pairs(): +def test_make_cmb_galaxy_combinations_only_symmetric_pairs() -> None: """Test that symmetric pairs are created for each compatible measurement.""" galaxy_bins = [ TomographicBin( @@ -1814,7 +1973,7 @@ def test_make_cmb_galaxy_combinations_only_symmetric_pairs(): assert not galaxy_first -def test_make_cmb_galaxy_combinations_only_single_galaxy_bin(): +def test_make_cmb_galaxy_combinations_only_single_galaxy_bin() -> None: """Test with a single galaxy bin.""" galaxy_bins = [ TomographicBin( @@ -1838,7 +1997,7 @@ def test_make_cmb_galaxy_combinations_only_single_galaxy_bin(): @pytest.mark.parametrize("cmb_name", ["cmb_convergence", "cmb_kappa", "planck_cmb"]) -def test_make_cmb_galaxy_combinations_only_parametrized_names(cmb_name: str): +def test_make_cmb_galaxy_combinations_only_parametrized_names(cmb_name: str) -> None: """Parametrized test for different CMB tracer names.""" galaxy_bins = [ TomographicBin( @@ -1861,7 +2020,7 @@ def test_make_cmb_galaxy_combinations_only_parametrized_names(cmb_name: str): assert combo.y.bin_name == cmb_name -def test_make_cmb_galaxy_combinations_only_vs_with_cmb(): +def test_make_cmb_galaxy_combinations_only_vs_with_cmb() -> None: """Test that this function is equivalent to make_all_photoz_bin_combinations_with_cmb without galaxy combinations.""" @@ -1914,7 +2073,7 @@ def test_make_cmb_galaxy_combinations_only_vs_with_cmb(): assert cmb_only_tuples == cmb_from_all_tuples -def test_make_all_photoz_bin_combinations_with_cmb_incompatible_measurements(): +def test_make_all_photoz_bin_combinations_with_cmb_incompatible_measurements() -> None: """Test that incompatible measurements are skipped in CMB-galaxy combinations.""" galaxy_bins = [ TomographicBin( @@ -1967,7 +2126,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_incompatible_measurements(): assert Galaxies.SHEAR_T in galaxy_measurements -def test_make_cmb_galaxy_combinations_only_incompatible_measurements(): +def test_make_cmb_galaxy_combinations_only_incompatible_measurements() -> None: """Test that incompatible measurements are skipped in CMB-galaxy combinations.""" galaxy_bins = [ TomographicBin( @@ -2006,7 +2165,7 @@ def test_make_cmb_galaxy_combinations_only_incompatible_measurements(): assert Galaxies.SHEAR_T in galaxy_measurements -def test_make_all_photoz_bin_combinations_with_cmb_all_incompatible(): +def test_make_all_photoz_bin_combinations_with_cmb_all_incompatible() -> None: """Test behavior when all galaxy measurements are incompatible with CMB.""" # Since we can't find truly incompatible measurements, let's test with # measurements that we know ARE compatible and adjust expectations @@ -2046,7 +2205,7 @@ def test_make_all_photoz_bin_combinations_with_cmb_all_incompatible(): assert len(combinations) == len(galaxy_only_combinations) + len(cmb_combinations) -def test_make_cmb_galaxy_combinations_only_all_incompatible(): +def test_make_cmb_galaxy_combinations_only_all_incompatible() -> None: """Test behavior when all galaxy measurements are incompatible with CMB.""" # Since we can't find truly incompatible measurements, let's test with # measurements that we know ARE compatible and adjust expectations @@ -2065,7 +2224,7 @@ def test_make_cmb_galaxy_combinations_only_all_incompatible(): assert len(combinations) == 1 -def test_make_all_photoz_bin_combinations_with_cmb_empty(): +def test_make_all_photoz_bin_combinations_with_cmb_empty() -> None: """Test behavior when given an empty list of galaxy bins.""" galaxy_bins = [ TomographicBin( @@ -2081,7 +2240,16 @@ def test_make_all_photoz_bin_combinations_with_cmb_empty(): assert len(combinations) == 0 -def test_extract_all_photoz_bin_combinations_with_selector(sacc_galaxy_cells): +def test_extract_all_photoz_bin_combinations_with_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_photoz_bin_combinations with bin_pair_selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2103,7 +2271,16 @@ def test_extract_all_photoz_bin_combinations_with_selector(sacc_galaxy_cells): assert len(auto_combinations) > 0 -def test_extract_all_photoz_bin_combinations_with_source_selector(sacc_galaxy_cells): +def test_extract_all_photoz_bin_combinations_with_source_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_photoz_bin_combinations with source bin selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2122,7 +2299,16 @@ def test_extract_all_photoz_bin_combinations_with_source_selector(sacc_galaxy_ce assert len(source_combinations) > 0 -def test_extract_all_photoz_bin_combinations_with_lens_selector(sacc_galaxy_cells): +def test_extract_all_photoz_bin_combinations_with_lens_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_photoz_bin_combinations with lens bin selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2140,7 +2326,16 @@ def test_extract_all_photoz_bin_combinations_with_lens_selector(sacc_galaxy_cell assert len(lens_combinations) > 0 -def test_extract_all_harmonic_metadata_with_selector(sacc_galaxy_cells): +def test_extract_all_harmonic_metadata_with_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_harmonic_metadata with bin_pair_selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2167,7 +2362,16 @@ def test_extract_all_harmonic_metadata_with_selector(sacc_galaxy_cells): assert isinstance(meta.ells, np.ndarray) -def test_extract_all_harmonic_metadata_with_source_selector(sacc_galaxy_cells): +def test_extract_all_harmonic_metadata_with_source_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_harmonic_metadata with source bin selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2184,7 +2388,16 @@ def test_extract_all_harmonic_metadata_with_source_selector(sacc_galaxy_cells): assert len(source_metadata) > 0 -def test_extract_all_harmonic_metadata_with_combined_selector(sacc_galaxy_cells): +def test_extract_all_harmonic_metadata_with_combined_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_harmonic_metadata with combined selectors.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2202,7 +2415,16 @@ def test_extract_all_harmonic_metadata_with_combined_selector(sacc_galaxy_cells) assert len(filtered_metadata) > 0 -def test_extract_all_real_metadata_with_selector(sacc_galaxy_xis): +def test_extract_all_real_metadata_with_selector( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_real_metadata with bin_pair_selector.""" sacc_data, _, _ = sacc_galaxy_xis @@ -2229,7 +2451,16 @@ def test_extract_all_real_metadata_with_selector(sacc_galaxy_xis): assert isinstance(meta.thetas, np.ndarray) -def test_extract_all_real_metadata_with_lens_selector(sacc_galaxy_xis): +def test_extract_all_real_metadata_with_lens_selector( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_real_metadata with lens bin selector.""" sacc_data, _, _ = sacc_galaxy_xis @@ -2247,7 +2478,16 @@ def test_extract_all_real_metadata_with_lens_selector(sacc_galaxy_xis): assert len(lens_metadata) > 0 -def test_extract_all_real_metadata_with_combined_selector(sacc_galaxy_xis): +def test_extract_all_real_metadata_with_combined_selector( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_real_metadata with combined selectors.""" sacc_data, _, _ = sacc_galaxy_xis @@ -2266,7 +2506,16 @@ def test_extract_all_real_metadata_with_combined_selector(sacc_galaxy_xis): assert len(filtered_metadata) > 0 -def test_extract_all_harmonic_metadata_with_not_selector(sacc_galaxy_cells): +def test_extract_all_harmonic_metadata_with_not_selector( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_harmonic_metadata with NOT selector.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2293,7 +2542,16 @@ def test_extract_all_harmonic_metadata_with_not_selector(sacc_galaxy_cells): assert len(auto_metadata) + len(cross_metadata) == len(all_metadata) -def test_extract_all_real_metadata_with_or_selector(sacc_galaxy_xis): +def test_extract_all_real_metadata_with_or_selector( + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_real_metadata with OR selector.""" sacc_data, _, _ = sacc_galaxy_xis @@ -2325,7 +2583,16 @@ def test_extract_all_real_metadata_with_or_selector(sacc_galaxy_xis): assert len(filtered_metadata) > 0 -def test_extract_with_selector_empty_result(sacc_galaxy_cells): +def test_extract_with_selector_empty_result( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test that selector can result in empty extraction.""" sacc_data, _, _ = sacc_galaxy_cells @@ -2344,7 +2611,16 @@ def test_extract_with_selector_empty_result(sacc_galaxy_cells): assert len(empty_harmonic) == 0 -def test_extract_with_selector_preserves_data_quality(sacc_galaxy_cells): +def test_extract_with_selector_preserves_data_quality( + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test that using selector doesn't affect data quality of extracted metadata.""" sacc_data, _, _ = sacc_galaxy_cells diff --git a/tests/metadata/test_metadata_two_point_types.py b/tests/metadata/test_metadata_two_point_types.py index 3637e675e..53c4b3e40 100644 --- a/tests/metadata/test_metadata_two_point_types.py +++ b/tests/metadata/test_metadata_two_point_types.py @@ -34,6 +34,7 @@ Clusters, Galaxies, Measurement, + TomographicBin, TracerNames, TwoPointHarmonic, TwoPointReal, @@ -61,9 +62,10 @@ from firecrown.metadata_types._sacc_type_string import ( _type_to_sacc_string_real as real, ) +from tests._types import SaccGalaxyWindowedFixture -def test_order_enums(): +def test_order_enums() -> None: assert compare_enums(CMB.CONVERGENCE, Clusters.COUNTS) < 0 assert compare_enums(Clusters.COUNTS, CMB.CONVERGENCE) > 0 @@ -80,7 +82,7 @@ def test_order_enums(): assert compare_enums(enumerand, enumerand) == 0 -def test_galaxies_is_shear(): +def test_galaxies_is_shear() -> None: assert Galaxies.PART_OF_XI_MINUS == Galaxies.SHEAR_MINUS assert Galaxies.PART_OF_XI_PLUS == Galaxies.SHEAR_PLUS assert Galaxies.SHEAR_E.is_shear() @@ -90,7 +92,7 @@ def test_galaxies_is_shear(): assert not Galaxies.COUNTS.is_shear() -def testcompare_enums_wrong_type(): +def testcompare_enums_wrong_type() -> None: with pytest.raises( ValueError, match=re.escape( @@ -101,22 +103,22 @@ def testcompare_enums_wrong_type(): compare_enums(Galaxies.COUNTS, 1) -def test_enumeration_equality_galaxy(): +def test_enumeration_equality_galaxy() -> None: for e1, e2 in product(Galaxies, chain(CMB, Clusters)): assert e1 != e2 -def test_enumeration_equality_cmb(): +def test_enumeration_equality_cmb() -> None: for e1, e2 in product(CMB, chain(Galaxies, Clusters)): assert e1 != e2 -def test_enumeration_equality_cluster(): +def test_enumeration_equality_cluster() -> None: for e1, e2 in product(Clusters, chain(CMB, Galaxies)): assert e1 != e2 -def test_exact_matches(): +def test_exact_matches() -> None: for sacc_name, space, (enum_1, enum_2) in snm.mappings: if space == "ell": assert harmonic(enum_1, enum_2) == sacc_name @@ -126,7 +128,7 @@ def test_exact_matches(): raise ValueError(f"Illegal 'space' value {space} in testing data") -def test_translation_order_dependence(): +def test_translation_order_dependence() -> None: for a, b in product(ALL_MEASUREMENTS, ALL_MEASUREMENTS): assert isinstance(a, (Galaxies, CMB, Clusters)) assert isinstance(b, (Galaxies, CMB, Clusters)) @@ -143,7 +145,7 @@ def test_translation_order_dependence(): assert harmonic(a, b) != real(a, b) -def test_unsupported_type_galaxy(): +def test_unsupported_type_galaxy() -> None: unknown_type = MagicMock() unknown_type.configure_mock(__eq__=MagicMock(return_value=False)) @@ -154,7 +156,7 @@ def test_unsupported_type_galaxy(): Galaxies.polarization(unknown_type) -def test_unsupported_type_cmb(): +def test_unsupported_type_cmb() -> None: unknown_type = MagicMock() unknown_type.configure_mock(__eq__=MagicMock(return_value=False)) @@ -165,7 +167,7 @@ def test_unsupported_type_cmb(): CMB.polarization(unknown_type) -def test_unsupported_type_cluster(): +def test_unsupported_type_cluster() -> None: unknown_type = MagicMock() unknown_type.configure_mock(__eq__=MagicMock(return_value=False)) @@ -176,7 +178,7 @@ def test_unsupported_type_cluster(): Clusters.polarization(unknown_type) -def test_type_hashs(): +def test_type_hashs() -> None: for e1, e2 in product(ALL_MEASUREMENTS, ALL_MEASUREMENTS): if e1 == e2: assert hash(e1) == hash(e2) @@ -184,7 +186,7 @@ def test_type_hashs(): assert hash(e1) != hash(e2) -def test_measurement_is_compatible(): +def test_measurement_is_compatible() -> None: for a, b in product(ALL_MEASUREMENTS, ALL_MEASUREMENTS): assert isinstance(a, (Galaxies, CMB, Clusters)) assert isinstance(b, (Galaxies, CMB, Clusters)) @@ -195,8 +197,15 @@ def test_measurement_is_compatible(): def test_extract_all_tracers_types_cells( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_cells tracers, _ = extract_all_measured_types(sacc_data) @@ -211,8 +220,8 @@ def test_extract_all_tracers_types_cells( def test_extract_all_tracers_types_cwindows( - sacc_galaxy_cwindows: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows tracers, _ = extract_all_measured_types(sacc_data) @@ -235,7 +244,7 @@ def test_extract_all_tracers_types_reals( tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], ], ], -): +) -> None: sacc_data, _, _ = sacc_galaxy_xis tracers, _ = extract_all_measured_types(sacc_data) @@ -261,7 +270,7 @@ def test_extract_all_tracers_types_reals_inverted( tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], ], ], -): +) -> None: sacc_data, _, _ = sacc_galaxy_xis_inverted with pytest.warns(DeprecationWarning, match="AUTO-CORRECTION PERFORMED"): @@ -287,7 +296,7 @@ def test_extract_all_tracers_types_cells_include_maybe( tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], ], ], -): +) -> None: sacc_data, _, _ = sacc_galaxy_cells assert extract_all_measured_types(sacc_data) == extract_all_measured_types( @@ -296,15 +305,8 @@ def test_extract_all_tracers_types_cells_include_maybe( def test_extract_all_tracers_types_cwindows_include_maybe( - sacc_galaxy_cwindows: tuple[ - sacc.Sacc, - dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], - dict[ - tuple[TracerNames, str], - tuple[npt.NDArray[np.int64], npt.NDArray[np.float64], sacc.BandpowerWindow], - ], - ], -): + sacc_galaxy_cwindows: SaccGalaxyWindowedFixture, +) -> None: sacc_data, _, _ = sacc_galaxy_cwindows assert extract_all_measured_types(sacc_data) == extract_all_measured_types( @@ -321,7 +323,7 @@ def test_extract_all_tracers_types_xi_thetas_include_maybe( tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], ], ], -): +) -> None: sacc_data, _, _ = sacc_galaxy_xis assert extract_all_measured_types(sacc_data) == extract_all_measured_types( @@ -329,7 +331,7 @@ def test_extract_all_tracers_types_xi_thetas_include_maybe( ) -def test_measurements_from_index1(): +def test_measurements_from_index1() -> None: index: TwoPointRealIndex = { "data_type": "galaxy_shearDensity_xi_t", "tracer_names": TracerNames("src0", "lens0"), @@ -342,7 +344,7 @@ def test_measurements_from_index1(): assert b == Galaxies.COUNTS -def test_measurements_from_index2(): +def test_measurements_from_index2() -> None: index: TwoPointRealIndex = { "data_type": "galaxy_shearDensity_xi_t", "tracer_names": TracerNames("lens0", "src0"), @@ -355,7 +357,7 @@ def test_measurements_from_index2(): assert b == Galaxies.COUNTS -def test_check_two_point_consistence_harmonic(two_point_cell: TwoPointHarmonic): +def test_check_two_point_consistence_harmonic(two_point_cell: TwoPointHarmonic) -> None: tpm = TwoPointMeasurement( data=np.zeros(100), indices=np.arange(100), @@ -367,7 +369,7 @@ def test_check_two_point_consistence_harmonic(two_point_cell: TwoPointHarmonic): def test_check_two_point_consistence_harmonic_real( optimized_two_point_real: TwoPointReal, -): +) -> None: tpm = TwoPointMeasurement( data=np.zeros(100), indices=np.arange(100), @@ -381,7 +383,9 @@ def test_check_two_point_consistence_harmonic_real( check_two_point_consistence_harmonic([tpm]) -def test_check_two_point_consistence_real(optimized_two_point_real: TwoPointReal): +def test_check_two_point_consistence_real( + optimized_two_point_real: TwoPointReal, +) -> None: tpm = TwoPointMeasurement( data=np.zeros(100), indices=np.arange(100), @@ -393,7 +397,7 @@ def test_check_two_point_consistence_real(optimized_two_point_real: TwoPointReal def test_check_two_point_consistence_real_harmonic( optimized_two_point_cell: TwoPointReal, -): +) -> None: tpm = TwoPointMeasurement( data=np.zeros(100), indices=np.arange(100), @@ -408,8 +412,15 @@ def test_check_two_point_consistence_real_harmonic( def test_check_two_point_consistence_harmonic_mixing_cov( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_cells tpms = extract_all_harmonic_data(sacc_data) @@ -427,8 +438,15 @@ def test_check_two_point_consistence_harmonic_mixing_cov( def test_check_two_point_consistence_real_mixing_cov( - sacc_galaxy_xis: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_xis tpms = extract_all_real_data(sacc_data) @@ -445,8 +463,15 @@ def test_check_two_point_consistence_real_mixing_cov( def test_check_two_point_consistence_harmonic_non_unique_indices( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_cells tpms = extract_all_harmonic_data(sacc_data) @@ -463,8 +488,15 @@ def test_check_two_point_consistence_harmonic_non_unique_indices( def test_check_two_point_consistence_real_non_unique_indices( - sacc_galaxy_xis: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_xis tpms = extract_all_real_data(sacc_data) @@ -480,8 +512,15 @@ def test_check_two_point_consistence_real_non_unique_indices( def test_check_two_point_consistence_harmonic_indices_overlap( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_cells tpms = extract_all_harmonic_data(sacc_data) @@ -498,8 +537,15 @@ def test_check_two_point_consistence_harmonic_indices_overlap( def test_check_two_point_consistence_real_indices_overlap( - sacc_galaxy_xis: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_xis: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]], + ], + ], +) -> None: sacc_data, _, _ = sacc_galaxy_xis tpms = extract_all_real_data(sacc_data) @@ -517,7 +563,7 @@ def test_check_two_point_consistence_real_indices_overlap( def test_extract_all_data_cells_order_convention( sacc_galaxy_cells_order_convention: sacc.Sacc, -): +) -> None: two_point_list = extract_all_harmonic_data(sacc_galaxy_cells_order_convention) assert len(two_point_list) == 1 @@ -525,7 +571,7 @@ def test_extract_all_data_cells_order_convention( assert two_point_list[0].metadata.XY.y.bin_name == "bin1" -def test_measurements_types_single_galaxy_lens(): +def test_measurements_types_single_galaxy_lens() -> None: """Test measurements_types with single Galaxy lens measurement type.""" measurements: set[Measurement] = {Galaxies.COUNTS} has_mixed, types = measurements_types(measurements) @@ -534,7 +580,7 @@ def test_measurements_types_single_galaxy_lens(): assert types == ["Galaxy lens"] -def test_measurements_types_single_galaxy_source(): +def test_measurements_types_single_galaxy_source() -> None: """Test measurements_types with single Galaxy source measurement type.""" measurements: set[Measurement] = {Galaxies.SHEAR_E} has_mixed, types = measurements_types(measurements) @@ -543,7 +589,7 @@ def test_measurements_types_single_galaxy_source(): assert types == ["Galaxy source"] -def test_measurements_types_single_cmb(): +def test_measurements_types_single_cmb() -> None: """Test measurements_types with single CMB measurement type.""" measurements: set[Measurement] = {CMB.CONVERGENCE} has_mixed, types = measurements_types(measurements) @@ -552,7 +598,7 @@ def test_measurements_types_single_cmb(): assert types == ["CMB"] -def test_measurements_types_single_cluster(): +def test_measurements_types_single_cluster() -> None: """Test measurements_types with single Cluster measurement type.""" measurements: set[Measurement] = {Clusters.COUNTS} has_mixed, types = measurements_types(measurements) @@ -561,7 +607,7 @@ def test_measurements_types_single_cluster(): assert types == ["Clusters"] -def test_measurements_types_multiple_galaxy_source(): +def test_measurements_types_multiple_galaxy_source() -> None: """Test measurements_types with multiple Galaxy source measurements (same type).""" measurements: set[Measurement] = { Galaxies.SHEAR_E, @@ -574,7 +620,7 @@ def test_measurements_types_multiple_galaxy_source(): assert types == ["Galaxy source"] -def test_measurements_types_galaxy_lens_and_source(): +def test_measurements_types_galaxy_lens_and_source() -> None: """Test measurements_types with both Galaxy lens and source (mixed types).""" measurements: set[Measurement] = {Galaxies.COUNTS, Galaxies.SHEAR_E} has_mixed, types = measurements_types(measurements) @@ -583,7 +629,7 @@ def test_measurements_types_galaxy_lens_and_source(): assert set(types) == {"Galaxy lens", "Galaxy source"} -def test_measurements_types_galaxy_and_cmb(): +def test_measurements_types_galaxy_and_cmb() -> None: """Test measurements_types with Galaxy and CMB (mixed types).""" measurements: set[Measurement] = {Galaxies.COUNTS, CMB.CONVERGENCE} has_mixed, types = measurements_types(measurements) @@ -592,7 +638,7 @@ def test_measurements_types_galaxy_and_cmb(): assert set(types) == {"Galaxy lens", "CMB"} -def test_measurements_types_galaxy_and_cluster(): +def test_measurements_types_galaxy_and_cluster() -> None: """Test measurements_types with Galaxy and Cluster (mixed types).""" measurements: set[Measurement] = {Galaxies.SHEAR_E, Clusters.COUNTS} has_mixed, types = measurements_types(measurements) @@ -601,7 +647,7 @@ def test_measurements_types_galaxy_and_cluster(): assert set(types) == {"Galaxy source", "Clusters"} -def test_measurements_types_cmb_and_cluster(): +def test_measurements_types_cmb_and_cluster() -> None: """Test measurements_types with CMB and Cluster (mixed types).""" measurements: set[Measurement] = {CMB.CONVERGENCE, Clusters.COUNTS} has_mixed, types = measurements_types(measurements) @@ -610,7 +656,7 @@ def test_measurements_types_cmb_and_cluster(): assert set(types) == {"CMB", "Clusters"} -def test_measurements_types_all_three_types(): +def test_measurements_types_all_three_types() -> None: """Test measurements_types with Galaxy, CMB, and Cluster (three types).""" measurements: set[Measurement] = {Galaxies.COUNTS, CMB.CONVERGENCE, Clusters.COUNTS} has_mixed, types = measurements_types(measurements) @@ -619,7 +665,7 @@ def test_measurements_types_all_three_types(): assert set(types) == {"Galaxy lens", "CMB", "Clusters"} -def test_measurements_types_all_four_categories(): +def test_measurements_types_all_four_categories() -> None: """Test measurements_types with Galaxy lens, Galaxy source, CMB, and Cluster.""" measurements: set[Measurement] = { Galaxies.COUNTS, # Galaxy lens @@ -633,7 +679,7 @@ def test_measurements_types_all_four_categories(): assert set(types) == {"Galaxy lens", "Galaxy source", "CMB", "Clusters"} -def test_measurements_types_empty_set(): +def test_measurements_types_empty_set() -> None: """Test measurements_types with empty measurement set.""" measurements: set[Measurement] = set() has_mixed, types = measurements_types(measurements) @@ -642,7 +688,7 @@ def test_measurements_types_empty_set(): assert not types -def test_measurements_types_order_independent(): +def test_measurements_types_order_independent() -> None: """Test that measurements_types returns consistent results. This test checks that measurements_types returns the same results regardless of set @@ -658,7 +704,7 @@ def test_measurements_types_order_independent(): assert set(types1) == set(types2) -def test_measurements_types_multiple_galaxy_and_cmb(): +def test_measurements_types_multiple_galaxy_and_cmb() -> None: """Test measurements_types with multiple Galaxy measurements and CMB.""" measurements: set[Measurement] = { Galaxies.COUNTS, # Galaxy lens @@ -673,8 +719,15 @@ def test_measurements_types_multiple_galaxy_and_cmb(): def test_extract_all_measured_mixed_types_with_genuinely_mixed_tracers( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test extract_all_measured_types with allow_mixed_types=True on mixed tracers. This test verifies that when allow_mixed_types=True, the function permits @@ -694,14 +747,14 @@ def test_extract_all_measured_mixed_types_with_genuinely_mixed_tracers( def test_extract_all_measured_mixed_types_galaxy_only( - sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells_src0_src0: tuple[sacc.Sacc, TomographicBin], +) -> None: """Test extract_all_measured_types with allow_mixed_types=True on single type. This test verifies that tracers with only one measurement type work correctly with allow_mixed_types=True. """ - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 + sacc_data, _ = sacc_galaxy_cells_src0_src0 result, _ = extract_all_measured_types(sacc_data, allow_mixed_types=True) @@ -710,16 +763,14 @@ def test_extract_all_measured_mixed_types_galaxy_only( def test_extract_all_measured_mixed_types_density_only( - sacc_galaxy_cells_lens0_lens0: tuple[ - sacc.Sacc, dict[str, object], dict[str, object] - ], -): + sacc_galaxy_cells_lens0_lens0: tuple[sacc.Sacc, TomographicBin], +) -> None: """Test extract_all_measured_types with allow_mixed_types=True on density. Verifies that galaxy density (COUNTS) measurements work correctly with allow_mixed_types=True. """ - sacc_data, _, _ = sacc_galaxy_cells_lens0_lens0 + sacc_data, _ = sacc_galaxy_cells_lens0_lens0 result, _ = extract_all_measured_types(sacc_data, allow_mixed_types=True) @@ -730,11 +781,11 @@ def test_extract_all_measured_mixed_types_density_only( def test_extract_all_measured_mixed_types_mixed_real_space( sacc_galaxy_xis_src0_lens0: tuple[ sacc.Sacc, - dict[str, object], - dict[str, object], - dict[str, object], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], ], -): +) -> None: """Test extract_all_measured_types with allow_mixed_types=True on real-space data. This test verifies behavior with real-space data (xi correlations) @@ -753,7 +804,7 @@ def test_extract_all_measured_mixed_types_mixed_real_space( assert Galaxies.COUNTS in result["lens0"] -def test_extract_all_measured_mixed_types_no_auto_correction(): +def test_extract_all_measured_mixed_types_no_auto_correction() -> None: """Test that allow_mixed_types=True doesn't trigger auto-correction. This test creates a SACC file where tracers have genuinely mixed types @@ -802,7 +853,7 @@ def test_extract_all_measured_mixed_types_no_auto_correction(): assert Galaxies.COUNTS in result["tracer1"] -def test_extract_all_measured_mixed_types_different_tracer_types(): +def test_extract_all_measured_mixed_types_different_tracer_types() -> None: """Test extract_all_measured_types with allow_mixed_types=True on different tracers. This test verifies that allow_mixed_types=True correctly processes @@ -839,7 +890,7 @@ def test_extract_all_measured_mixed_types_different_tracer_types(): assert Galaxies.COUNTS in result["lens_bin"] -def test_extract_all_measured_mixed_types_multiple_measurements_same_tracer(): +def test_extract_all_measured_mixed_types_multiple_measurements_same_tracer() -> None: """Test extract_all_measured_types with multiple measurement types per tracer. This test verifies that a tracer can simultaneously measure different @@ -877,8 +928,15 @@ def test_extract_all_measured_mixed_types_multiple_measurements_same_tracer(): def test_extract_all_measured_types_allow_mixed_types_vs_false( - sacc_galaxy_cells: tuple[sacc.Sacc, dict[str, object], dict[str, object]], -): + sacc_galaxy_cells: tuple[ + sacc.Sacc, + dict[str, tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]], + dict[ + tuple[TracerNames, str], + tuple[npt.NDArray[np.int64], npt.NDArray[np.float64]], + ], + ], +) -> None: """Test the difference between allow_mixed_types=True and False. This test verifies that with allow_mixed_types=False, we may get @@ -906,7 +964,7 @@ def test_extract_all_measured_types_allow_mixed_types_vs_false( pass -def test_extract_all_measured_types_raises_on_mixed_types_when_not_allowed(): +def test_extract_all_measured_types_raises_on_mixed_types_when_not_allowed() -> None: """Test that extract_all_measured_types raises ValueError for mixed types. This test creates a SACC file with a tracer that has genuinely mixed diff --git a/tests/models/cluster/test_abundance_data.py b/tests/models/cluster/test_abundance_data.py index 617cb596d..acbe65407 100644 --- a/tests/models/cluster/test_abundance_data.py +++ b/tests/models/cluster/test_abundance_data.py @@ -9,7 +9,7 @@ ) -def test_create_abundance_data(): +def test_create_abundance_data() -> None: s = sacc.Sacc() ad = AbundanceData(s) @@ -21,7 +21,9 @@ def test_create_abundance_data(): assert ad._survey_index == 0 -def test_get_survey_tracer_missing_survey_name(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_missing_survey_name( + cluster_sacc_data: sacc.Sacc, +) -> None: ad = AbundanceData(cluster_sacc_data) with pytest.raises( ValueError, @@ -30,7 +32,9 @@ def test_get_survey_tracer_missing_survey_name(cluster_sacc_data: sacc.Sacc): ad.get_survey_tracer("the_black_lodge") -def test_get_survey_tracer_wrong_tracer_type(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_wrong_tracer_type( + cluster_sacc_data: sacc.Sacc, +) -> None: ad = AbundanceData(cluster_sacc_data) with pytest.raises( ValueError, @@ -39,26 +43,28 @@ def test_get_survey_tracer_wrong_tracer_type(cluster_sacc_data: sacc.Sacc): ad.get_survey_tracer("z_bin_tracer_1") -def test_get_survey_tracer_returns_survey_tracer(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_returns_survey_tracer( + cluster_sacc_data: sacc.Sacc, +) -> None: ad = AbundanceData(cluster_sacc_data) survey = ad.get_survey_tracer("my_survey") assert survey is not None assert isinstance(survey, sacc.tracers.SurveyTracer) -def test_get_bin_edges_cluster_counts(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_cluster_counts(cluster_sacc_data: sacc.Sacc) -> None: ad = AbundanceData(cluster_sacc_data) bins = ad.get_bin_edges("my_survey", ClusterProperty.COUNTS) assert len(bins) == 2 -def test_get_bin_edges_cluster_mass(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_cluster_mass(cluster_sacc_data: sacc.Sacc) -> None: ad = AbundanceData(cluster_sacc_data) bins = ad.get_bin_edges("my_survey", ClusterProperty.MASS) assert len(bins) == 2 -def test_get_bin_edges_counts_and_mass(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_counts_and_mass(cluster_sacc_data: sacc.Sacc) -> None: ad = AbundanceData(cluster_sacc_data) bins = ad.get_bin_edges( "my_survey", (ClusterProperty.MASS | ClusterProperty.COUNTS) @@ -66,7 +72,9 @@ def test_get_bin_edges_counts_and_mass(cluster_sacc_data: sacc.Sacc): assert len(bins) == 2 -def test_get_bin_edges_counts_and_mass_and_shear(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_counts_and_mass_and_shear( + cluster_sacc_data: sacc.Sacc, +) -> None: ad = AbundanceData(cluster_sacc_data) bins = ad.get_bin_edges( "my_survey", @@ -77,7 +85,7 @@ def test_get_bin_edges_counts_and_mass_and_shear(cluster_sacc_data: sacc.Sacc): def test_observed_data_and_indices_by_survey_cluster_counts( cluster_sacc_data: sacc.Sacc, -): +) -> None: ad = AbundanceData(cluster_sacc_data) data, indices = ad.get_observed_data_and_indices_by_survey( "my_survey", ClusterProperty.COUNTS @@ -88,7 +96,7 @@ def test_observed_data_and_indices_by_survey_cluster_counts( def test_observed_data_and_indices_by_survey_cluster_mass( cluster_sacc_data: sacc.Sacc, -): +) -> None: ad = AbundanceData(cluster_sacc_data) data, indices = ad.get_observed_data_and_indices_by_survey( "my_survey", ClusterProperty.MASS @@ -99,7 +107,7 @@ def test_observed_data_and_indices_by_survey_cluster_mass( def test_observed_data_and_indices_by_survey_cluster_counts_and_mass( cluster_sacc_data: sacc.Sacc, -): +) -> None: ad = AbundanceData(cluster_sacc_data) data, indices = ad.get_observed_data_and_indices_by_survey( "my_survey", ClusterProperty.MASS | ClusterProperty.COUNTS @@ -108,7 +116,7 @@ def test_observed_data_and_indices_by_survey_cluster_counts_and_mass( assert len(indices) == 4 -def test_observed_data_and_indices_no_data_throws(): +def test_observed_data_and_indices_no_data_throws() -> None: # pylint: disable=no-member cc = sacc.standard_types.cluster_counts @@ -138,7 +146,9 @@ def test_observed_data_and_indices_no_data_throws(): ad.get_observed_data_and_indices_by_survey("my_survey", ClusterProperty.MASS) -def test_observed_data_and_indices_redshift_property(cluster_sacc_data: sacc.Sacc): +def test_observed_data_and_indices_redshift_property( + cluster_sacc_data: sacc.Sacc, +) -> None: """Test that REDSHIFT property is handled correctly (not COUNTS or MASS).""" ad = AbundanceData(cluster_sacc_data) # REDSHIFT is valid but not handled in get_observed_data_and_indices_by_survey diff --git a/tests/models/cluster/test_binning.py b/tests/models/cluster/test_binning.py index be560dc1c..004556ac9 100644 --- a/tests/models/cluster/test_binning.py +++ b/tests/models/cluster/test_binning.py @@ -8,7 +8,7 @@ from firecrown.models.cluster import NDimensionalBin, SaccBin, TupleBin -def test_bin_str(): +def test_bin_str() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -16,7 +16,7 @@ def test_bin_str(): assert str(sb) == "[(0, 1), (4, 5), (1, 2)]\n" -def test_create_sacc_bin_with_correct_dimension(): +def test_create_sacc_bin_with_correct_dimension() -> None: tracer = sacc.tracers.SurveyTracer("", 1) for i in range(10): tracers = [tracer for _ in range(i)] @@ -25,7 +25,7 @@ def test_create_sacc_bin_with_correct_dimension(): assert sb.dimension == i -def test_sacc_bin_z_edges(): +def test_sacc_bin_z_edges() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -45,7 +45,7 @@ def test_sacc_bin_z_edges(): assert sb.z_edges == (0, 1) -def test_sacc_bin_z_edges_throws_when_multiple_z_bins(): +def test_sacc_bin_z_edges_throws_when_multiple_z_bins() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -54,7 +54,7 @@ def test_sacc_bin_z_edges_throws_when_multiple_z_bins(): print(sb.z_edges) -def test_sacc_bin_mass_proxy_edges_throws_when_multiple_mass_proxy_bins(): +def test_sacc_bin_mass_proxy_edges_throws_when_multiple_mass_proxy_bins() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -63,7 +63,7 @@ def test_sacc_bin_mass_proxy_edges_throws_when_multiple_mass_proxy_bins(): print(sb.mass_proxy_edges) -def test_sacc_bin_radius_edges_throws_when_multiple_radius_bins(): +def test_sacc_bin_radius_edges_throws_when_multiple_radius_bins() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -72,7 +72,7 @@ def test_sacc_bin_radius_edges_throws_when_multiple_radius_bins(): print(sb.radius_edges) -def test_sacc_bin_radius_center_throws_when_multiple_radius_bins(): +def test_sacc_bin_radius_center_throws_when_multiple_radius_bins() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -81,7 +81,7 @@ def test_sacc_bin_radius_center_throws_when_multiple_radius_bins(): print(sb.radius_center) -def test_sacc_bin_richness_edges(): +def test_sacc_bin_richness_edges() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) sb = SaccBin([tracer_z, tracer_lambda]) @@ -93,7 +93,7 @@ def test_sacc_bin_richness_edges(): assert sb.mass_proxy_edges == (4, 5) -def test_sacc_bin_radius_edges(): +def test_sacc_bin_radius_edges() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -113,7 +113,7 @@ def test_sacc_bin_radius_edges(): assert sb.radius_edges == (1, 2) -def test_sacc_bin_radius_center(): +def test_sacc_bin_radius_center() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) tracer_radius = sacc.tracers.BinRadiusTracer("", 1, 2, 1.5) @@ -136,7 +136,7 @@ def test_sacc_bin_radius_center(): assert radius_center == 1.5 -def test_equal_sacc_bins_are_equal(): +def test_equal_sacc_bins_are_equal() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) sb1 = SaccBin([tracer_z, tracer_lambda]) @@ -152,7 +152,7 @@ def test_equal_sacc_bins_are_equal(): assert sb1 is not sb2 -def test_sacc_bin_different_edges_not_equal(): +def test_sacc_bin_different_edges_not_equal() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) sb1 = SaccBin([tracer_z, tracer_lambda]) @@ -176,7 +176,7 @@ def test_sacc_bin_different_edges_not_equal(): assert sb1 != sb2 -def test_sacc_bin_different_dimensions_not_equal(): +def test_sacc_bin_different_dimensions_not_equal() -> None: tracer_z = sacc.tracers.BinZTracer("", 0, 1) tracer_z2 = sacc.tracers.BinZTracer("", 1, 2) tracer_lambda = sacc.tracers.BinRichnessTracer("", 4, 5) @@ -186,7 +186,7 @@ def test_sacc_bin_different_dimensions_not_equal(): assert sb1 != sb2 -def test_sacc_bin_must_be_equal_type(): +def test_sacc_bin_must_be_equal_type() -> None: other_bin = Mock(spec=NDimensionalBin) tracer_z = sacc.tracers.BinZTracer("", 0, 1) @@ -196,7 +196,7 @@ def test_sacc_bin_must_be_equal_type(): assert sb != other_bin -def test_create_tuple_bin(): +def test_create_tuple_bin() -> None: tb = TupleBin([(1, 2), (3, 4), (1, 2, 1.5)]) assert tb is not None assert tb.dimension == 3 @@ -206,7 +206,7 @@ def test_create_tuple_bin(): assert tb.radius_center == 1.5 -def test_tuple_bin_radius_center_requires_center_value(): +def test_tuple_bin_radius_center_requires_center_value() -> None: tb = TupleBin([(1, 2), (3, 4), (1, 2)]) with pytest.raises( @@ -215,7 +215,7 @@ def test_tuple_bin_radius_center_requires_center_value(): _ = tb.radius_center -def test_tuple_bins_are_equal(): +def test_tuple_bins_are_equal() -> None: tb1 = TupleBin([(1, 2), (3, 4)]) tb2 = TupleBin([(1, 2), (3, 4)]) assert tb1 == tb2 @@ -223,7 +223,7 @@ def test_tuple_bins_are_equal(): assert tb1 is not tb2 -def test_tuple_bin_neq_sacc_bin(): +def test_tuple_bin_neq_sacc_bin() -> None: tb = TupleBin([(1, 2), (3, 4)]) tracer_z = sacc.tracers.BinZTracer("", 3, 4) tracer_lambda = sacc.tracers.BinRichnessTracer("", 1, 2) @@ -231,7 +231,7 @@ def test_tuple_bin_neq_sacc_bin(): assert tb != sb -def test_tuple_bin_different_dimensions_not_equal(): +def test_tuple_bin_different_dimensions_not_equal() -> None: tb1 = TupleBin([(1, 2), (3, 4), (5, 6)]) tb2 = TupleBin([(1, 2), (3, 4)]) assert tb1 != tb2 @@ -241,7 +241,7 @@ def test_tuple_bin_different_dimensions_not_equal(): assert tb1 != tb2 -def test_tuple_bin_different_bins_not_equal(): +def test_tuple_bin_different_bins_not_equal() -> None: tb1 = TupleBin([(1, 2), (3, 4)]) tb2 = TupleBin([(1, 2), (3, 5)]) assert tb1 != tb2 diff --git a/tests/models/cluster/test_shear_data.py b/tests/models/cluster/test_shear_data.py index 1fb0d3221..af270603e 100644 --- a/tests/models/cluster/test_shear_data.py +++ b/tests/models/cluster/test_shear_data.py @@ -9,7 +9,7 @@ ) -def test_create_deltasigma_data(): +def test_create_deltasigma_data() -> None: s = sacc.Sacc() dsd = ShearData(s) @@ -23,7 +23,9 @@ def test_create_deltasigma_data(): assert dsd._radius_index == 3 -def test_get_survey_tracer_missing_survey_name(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_missing_survey_name( + cluster_sacc_data: sacc.Sacc, +) -> None: dsd = ShearData(cluster_sacc_data) with pytest.raises( ValueError, @@ -32,7 +34,9 @@ def test_get_survey_tracer_missing_survey_name(cluster_sacc_data: sacc.Sacc): dsd.get_survey_tracer("the_black_lodge") -def test_get_survey_tracer_wrong_tracer_type(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_wrong_tracer_type( + cluster_sacc_data: sacc.Sacc, +) -> None: dsd = ShearData(cluster_sacc_data) with pytest.raises( ValueError, @@ -65,14 +69,16 @@ def test_wrong_tracer_number(cluster_sacc_data: sacc.Sacc) -> None: dsd._all_bin_combinations_for_data_type(cs, tracers_n) -def test_get_survey_tracer_returns_survey_tracer(cluster_sacc_data: sacc.Sacc): +def test_get_survey_tracer_returns_survey_tracer( + cluster_sacc_data: sacc.Sacc, +) -> None: dsd = ShearData(cluster_sacc_data) survey = dsd.get_survey_tracer("my_survey") assert survey is not None assert isinstance(survey, sacc.tracers.SurveyTracer) -def test_get_bin_edges_cluster_radius(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_cluster_radius(cluster_sacc_data: sacc.Sacc) -> None: dsd = ShearData(cluster_sacc_data) bins = dsd.get_bin_edges("my_survey", ClusterProperty.DELTASIGMA) assert len(bins) == 2 @@ -80,7 +86,7 @@ def test_get_bin_edges_cluster_radius(cluster_sacc_data: sacc.Sacc): def test_observed_data_and_indices_by_survey_cluster_deltasigma( cluster_sacc_data: sacc.Sacc, -): +) -> None: dsd = ShearData(cluster_sacc_data) data, indices = dsd.get_observed_data_and_indices_by_survey( "my_survey", ClusterProperty.DELTASIGMA @@ -89,7 +95,7 @@ def test_observed_data_and_indices_by_survey_cluster_deltasigma( assert len(indices) == 2 -def test_get_bin_edges_cluster_shear(cluster_sacc_data: sacc.Sacc): +def test_get_bin_edges_cluster_shear(cluster_sacc_data: sacc.Sacc) -> None: dsd = ShearData(cluster_sacc_data) bins = dsd.get_bin_edges("my_survey", ClusterProperty.SHEAR) assert len(bins) == 2 @@ -97,7 +103,7 @@ def test_get_bin_edges_cluster_shear(cluster_sacc_data: sacc.Sacc): def test_observed_data_and_indices_by_survey_cluster_shear( cluster_sacc_data: sacc.Sacc, -): +) -> None: dsd = ShearData(cluster_sacc_data) data, indices = dsd.get_observed_data_and_indices_by_survey( "my_survey", ClusterProperty.SHEAR @@ -106,7 +112,7 @@ def test_observed_data_and_indices_by_survey_cluster_shear( assert len(indices) == 2 -def test_observed_data_and_indices_wrong_property(): +def test_observed_data_and_indices_wrong_property() -> None: """Test error when wrong property used with ShearData.""" s = sacc.Sacc() ads = ShearData(s) @@ -122,7 +128,7 @@ def test_observed_data_and_indices_wrong_property(): def test_observed_data_and_indices_both_deltasigma_and_shear( cluster_sacc_data: sacc.Sacc, -): +) -> None: """Ensure error is raised if both DELTASIGMA and SHEAR are requested at once.""" dsd = ShearData(cluster_sacc_data) with pytest.raises( diff --git a/tests/models/two_point/test_interpolation.py b/tests/models/two_point/test_interpolation.py index 940e2018e..cd5e77201 100644 --- a/tests/models/two_point/test_interpolation.py +++ b/tests/models/two_point/test_interpolation.py @@ -6,7 +6,7 @@ from firecrown.models.two_point import ApplyInterpolationWhen -def test_apply_interpolation_when_none(): +def test_apply_interpolation_when_none() -> None: """Test ApplyInterpolationWhen.NONE flag.""" apply_interp_when = ApplyInterpolationWhen.NONE assert apply_interp_when == ApplyInterpolationWhen.NONE @@ -15,7 +15,7 @@ def test_apply_interpolation_when_none(): assert not apply_interp_when & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_all(): +def test_apply_interpolation_when_all() -> None: """Test ApplyInterpolationWhen.ALL flag.""" apply_interp_when = ApplyInterpolationWhen.ALL assert apply_interp_when == ApplyInterpolationWhen.ALL @@ -24,7 +24,7 @@ def test_apply_interpolation_when_all(): assert apply_interp_when & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_default(): +def test_apply_interpolation_when_default() -> None: """Test ApplyInterpolationWhen.DEFAULT flag composition.""" apply_interp_when = ApplyInterpolationWhen.DEFAULT assert apply_interp_when == ApplyInterpolationWhen.DEFAULT @@ -33,7 +33,7 @@ def test_apply_interpolation_when_default(): assert not apply_interp_when & ApplyInterpolationWhen.HARMONIC -def test_apply_interpolation_when_real(): +def test_apply_interpolation_when_real() -> None: """Test ApplyInterpolationWhen.REAL flag.""" apply_interp_when = ApplyInterpolationWhen.REAL assert apply_interp_when & ApplyInterpolationWhen.REAL @@ -41,7 +41,7 @@ def test_apply_interpolation_when_real(): assert not apply_interp_when & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_harmonic(): +def test_apply_interpolation_when_harmonic() -> None: """Test ApplyInterpolationWhen.HARMONIC flag.""" apply_interp_when = ApplyInterpolationWhen.HARMONIC assert not apply_interp_when & ApplyInterpolationWhen.REAL @@ -49,7 +49,7 @@ def test_apply_interpolation_when_harmonic(): assert not apply_interp_when & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_harmonic_window(): +def test_apply_interpolation_when_harmonic_window() -> None: """Test ApplyInterpolationWhen.HARMONIC_WINDOW flag.""" apply_interp_when = ApplyInterpolationWhen.HARMONIC_WINDOW assert not apply_interp_when & ApplyInterpolationWhen.REAL @@ -57,7 +57,7 @@ def test_apply_interpolation_when_harmonic_window(): assert apply_interp_when & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_bitwise_or(): +def test_apply_interpolation_when_bitwise_or() -> None: """Test bitwise OR operations on ApplyInterpolationWhen flags.""" combined = ApplyInterpolationWhen.REAL | ApplyInterpolationWhen.HARMONIC assert combined & ApplyInterpolationWhen.REAL @@ -65,7 +65,7 @@ def test_apply_interpolation_when_bitwise_or(): assert not combined & ApplyInterpolationWhen.HARMONIC_WINDOW -def test_apply_interpolation_when_iteration(): +def test_apply_interpolation_when_iteration() -> None: """Test that all ApplyInterpolationWhen values are iterable.""" all_values = list(ApplyInterpolationWhen) assert len(all_values) >= 3 # At minimum: REAL, HARMONIC, HARMONIC_WINDOW @@ -74,7 +74,7 @@ def test_apply_interpolation_when_iteration(): assert ApplyInterpolationWhen.HARMONIC_WINDOW in all_values -def test_pydantic_schema_exists(): +def test_pydantic_schema_exists() -> None: """Test that Pydantic core schema can be retrieved.""" result = ApplyInterpolationWhen.__get_pydantic_core_schema__( ApplyInterpolationWhen, None # type: ignore[arg-type] @@ -82,7 +82,7 @@ def test_pydantic_schema_exists(): assert result is not None -def test_pydantic_model_with_apply_interpolation_when(): +def test_pydantic_model_with_apply_interpolation_when() -> None: """Test using ApplyInterpolationWhen in a Pydantic model.""" class TestModel(BaseModel): @@ -108,7 +108,7 @@ class TestModel(BaseModel): assert model4.interp == ApplyInterpolationWhen.NONE -def test_pydantic_serialization_with_model(): +def test_pydantic_serialization_with_model() -> None: """Test serialization of ApplyInterpolationWhen in Pydantic model.""" class TestModel(BaseModel): @@ -134,7 +134,7 @@ class TestModel(BaseModel): assert "HARMONIC" in data3["interp"] -def test_pydantic_validation_errors(): +def test_pydantic_validation_errors() -> None: """Test that invalid inputs raise proper validation errors.""" class TestModel(BaseModel): @@ -151,7 +151,7 @@ class TestModel(BaseModel): TestModel(interp=123) # type: ignore[arg-type] -def test_pydantic_serialize_none(): +def test_pydantic_serialize_none() -> None: """Test Pydantic serialization of NONE flag.""" schema = ApplyInterpolationWhen.__get_pydantic_core_schema__( ApplyInterpolationWhen, None # type: ignore[arg-type] @@ -164,7 +164,7 @@ def test_pydantic_serialize_none(): assert result == "NONE" -def test_pydantic_serialize_single_flag(): +def test_pydantic_serialize_single_flag() -> None: """Test Pydantic serialization of a single flag.""" schema = ApplyInterpolationWhen.__get_pydantic_core_schema__( ApplyInterpolationWhen, None # type: ignore[arg-type] @@ -176,7 +176,7 @@ def test_pydantic_serialize_single_flag(): assert result == "REAL" -def test_pydantic_serialize_multiple_flags(): +def test_pydantic_serialize_multiple_flags() -> None: """Test Pydantic serialization of combined flags.""" schema = ApplyInterpolationWhen.__get_pydantic_core_schema__( ApplyInterpolationWhen, None # type: ignore[arg-type] @@ -193,7 +193,7 @@ def test_pydantic_serialize_multiple_flags(): assert "|" in result -def test_pydantic_serialize_default(): +def test_pydantic_serialize_default() -> None: """Test Pydantic serialization of DEFAULT flag.""" schema = ApplyInterpolationWhen.__get_pydantic_core_schema__( ApplyInterpolationWhen, None # type: ignore[arg-type] diff --git a/tests/models/two_point/test_power_spectrum.py b/tests/models/two_point/test_power_spectrum.py index 5f03092da..05b1866c0 100644 --- a/tests/models/two_point/test_power_spectrum.py +++ b/tests/models/two_point/test_power_spectrum.py @@ -19,7 +19,7 @@ @pytest.fixture -def mock_tools(): +def mock_tools() -> Mock: """Create a mock ModelingTools object.""" tools = Mock(spec=ModelingTools) @@ -39,7 +39,7 @@ def mock_tools(): @pytest.fixture -def mock_tracer_with_hm(): +def mock_tracer_with_hm() -> Mock: """Create a mock tracer that has halo model.""" tracer = Mock(spec=Tracer) tracer.has_hm = True @@ -55,7 +55,7 @@ def mock_tracer_with_hm(): @pytest.fixture -def mock_tracer_without_hm(): +def mock_tracer_without_hm() -> Mock: """Create a mock tracer that does not have halo model.""" tracer = Mock(spec=Tracer) tracer.has_hm = False @@ -65,7 +65,7 @@ def mock_tracer_without_hm(): return tracer -def test_both_tracers_have_hm(mock_tools, mock_tracer_with_hm): +def test_both_tracers_have_hm(mock_tools: Mock, mock_tracer_with_hm: Mock) -> None: """Test case where both tracers have halo model.""" tracer0 = mock_tracer_with_hm tracer1 = mock_tracer_with_hm @@ -95,8 +95,10 @@ def test_both_tracers_have_hm(mock_tools, mock_tracer_with_hm): def test_only_first_tracer_has_hm( - mock_tools, mock_tracer_with_hm, mock_tracer_without_hm -): + mock_tools: Mock, + mock_tracer_with_hm: Mock, + mock_tracer_without_hm: Mock, +) -> None: """Test case where only the first tracer has halo model.""" tracer0 = mock_tracer_with_hm tracer1 = mock_tracer_without_hm @@ -120,8 +122,10 @@ def test_only_first_tracer_has_hm( def test_only_second_tracer_has_hm( - mock_tools, mock_tracer_with_hm, mock_tracer_without_hm -): + mock_tools: Mock, + mock_tracer_with_hm: Mock, + mock_tracer_without_hm: Mock, +) -> None: """Test case where only the second tracer has halo model.""" tracer0 = mock_tracer_without_hm tracer1 = mock_tracer_with_hm @@ -144,7 +148,7 @@ def test_only_second_tracer_has_hm( assert result == mock_pk_total -def test_mixed_tracer_requires_shear(mock_tools): +def test_mixed_tracer_requires_shear(mock_tools: Mock) -> None: """Test that when not both tracers have HM, at least one must be 'shear'.""" # Create two tracers without HM and without shear to trigger AssertionError tracer0 = Mock(spec=Tracer) @@ -165,7 +169,9 @@ def test_mixed_tracer_requires_shear(mock_tools): at_least_one_tracer_has_hm(mock_tools, tracer0, tracer1) -def test_profile_assertion_for_mixed_hm(mock_tools, mock_tracer_without_hm): +def test_profile_assertion_for_mixed_hm( + mock_tools: Mock, mock_tracer_without_hm: Mock +) -> None: """Test assertion when HM tracer has None halo_profile in mixed case.""" # Create HM tracer with None halo_profile tracer0 = Mock(spec=Tracer) @@ -180,7 +186,7 @@ def test_profile_assertion_for_mixed_hm(mock_tools, mock_tracer_without_hm): at_least_one_tracer_has_hm(mock_tools, tracer0, tracer1) -def test_ia_bias_exponent_both_hm(mock_tools, mock_tracer_with_hm): +def test_ia_bias_exponent_both_hm(mock_tools: Mock, mock_tracer_with_hm: Mock) -> None: """Test that IA bias exponent is 2 when both tracers have HM.""" tracer0 = mock_tracer_with_hm tracer1 = mock_tracer_with_hm @@ -221,8 +227,10 @@ def test_ia_bias_exponent_both_hm(mock_tools, mock_tracer_with_hm): def test_halo_profile_nfw_configuration( - mock_tools, mock_tracer_with_hm, mock_tracer_without_hm -): + mock_tools: Mock, + mock_tracer_with_hm: Mock, + mock_tracer_without_hm: Mock, +) -> None: """Test that HaloProfileNFW is configured correctly for non-HM tracers.""" tracer0 = mock_tracer_with_hm tracer1 = mock_tracer_without_hm @@ -257,7 +265,7 @@ def test_halo_profile_nfw_configuration( assert result == mock_pk_total -def test_pk2d_addition_result(mock_tools, mock_tracer_with_hm): +def test_pk2d_addition_result(mock_tools: Mock, mock_tracer_with_hm: Mock) -> None: """Test that the function returns the sum of 1-halo and 2-halo power spectra.""" tracer0 = mock_tracer_with_hm tracer1 = mock_tracer_with_hm @@ -285,7 +293,7 @@ def test_pk2d_addition_result(mock_tools, mock_tracer_with_hm): assert result is mock_pk_total # Ensure it's the exact same object -def test_calculate_pk_with_existing_pk(mock_tools): +def test_calculate_pk_with_existing_pk(mock_tools: Mock) -> None: """Test calculate_pk when power spectrum already exists in tools.""" pk_name = "existing_pk" @@ -303,7 +311,7 @@ def test_calculate_pk_with_existing_pk(mock_tools): assert result == mock_pk -def test_calculate_pk_with_pt_tracers(): +def test_calculate_pk_with_pt_tracers() -> None: """Test calculate_pk when at least one tracer has PT.""" pk_name = "pt_pk" @@ -330,7 +338,7 @@ def test_calculate_pk_with_pt_tracers(): assert result == mock_pk -def test_calculate_pk_with_hm_tracers(): +def test_calculate_pk_with_hm_tracers() -> None: """Test calculate_pk when at least one tracer has HM.""" pk_name = "hm_pk" @@ -357,7 +365,7 @@ def test_calculate_pk_with_hm_tracers(): assert result == mock_pk -def test_calculate_pk_no_valid_method(): +def test_calculate_pk_no_valid_method() -> None: """Test calculate_pk raises ValueError when no valid method exists.""" pk_name = "invalid_pk" @@ -376,7 +384,7 @@ def test_calculate_pk_no_valid_method(): calculate_pk(pk_name, mock_tools, tracer0, tracer1) -def test_at_least_one_tracer_has_pt_both_have_pt(): +def test_at_least_one_tracer_has_pt_both_have_pt() -> None: """Test at_least_one_tracer_has_pt when both tracers have PT.""" mock_tools = Mock(spec=ModelingTools) @@ -399,7 +407,7 @@ def test_at_least_one_tracer_has_pt_both_have_pt(): assert result == mock_pk -def test_at_least_one_tracer_has_pt_first_missing_pt(): +def test_at_least_one_tracer_has_pt_first_missing_pt() -> None: """Test at_least_one_tracer_has_pt when first tracer does not have PT.""" mock_tools = Mock(spec=ModelingTools) @@ -423,7 +431,7 @@ def test_at_least_one_tracer_has_pt_first_missing_pt(): assert result == mock_pk -def test_at_least_one_tracer_has_pt_second_missing_pt(): +def test_at_least_one_tracer_has_pt_second_missing_pt() -> None: """Test at_least_one_tracer_has_pt when second tracer does not have PT.""" mock_tools = Mock(spec=ModelingTools) diff --git a/tests/models/two_point/test_sacc_utils.py b/tests/models/two_point/test_sacc_utils.py index 53b69a131..afab996e7 100644 --- a/tests/models/two_point/test_sacc_utils.py +++ b/tests/models/two_point/test_sacc_utils.py @@ -8,7 +8,7 @@ import firecrown.models.two_point as models -def test_determine_ccl_kind(): +def test_determine_ccl_kind() -> None: assert models.determine_ccl_kind("galaxy_density_cl") == "cl" assert models.determine_ccl_kind("galaxy_shearDensity_cl_e") == "cl" assert models.determine_ccl_kind("galaxy_shear_cl_ee") == "cl" @@ -27,7 +27,7 @@ def test_determine_ccl_kind(): def test_calculate_pk_lacking_pk( tools_with_vanilla_cosmology: mt.ModelingTools, empty_pyccl_tracer: pyccl.Tracer -): +) -> None: dummy = source.Tracer(empty_pyccl_tracer) with pytest.raises(ValueError): _ = models.calculate_pk( diff --git a/tests/models/two_point/test_theory.py b/tests/models/two_point/test_theory.py index 18d1400d9..50e21a17e 100644 --- a/tests/models/two_point/test_theory.py +++ b/tests/models/two_point/test_theory.py @@ -12,24 +12,28 @@ from firecrown.modeling_tools import ModelingTools from firecrown.models.two_point import ApplyInterpolationWhen, TwoPointTheory from firecrown.updatable import ParamsMap +from firecrown.updatable import UpdatableUsageRecord from firecrown.utils import ClIntegrationOptions # pylint: disable=protected-access @pytest.fixture(name="mock_source") -def fixture_mock_source(): +def fixture_mock_source() -> Mock: """Create a mock source that behaves like a real Updatable Source.""" source = Mock(spec=Source) source.sacc_tracer = "tracer_1" source.is_updated = Mock(return_value=False) - def update_side_effect(_params, _updated_record=None): + def update_side_effect( + _params: ParamsMap, + _updated_record: list[UpdatableUsageRecord] | None = None, + ) -> None: if source.is_updated(): return # Mimic Updatable: do nothing if already updated source.is_updated.return_value = True - def reset_side_effect(): + def reset_side_effect() -> None: source.is_updated.return_value = False source.update = Mock(side_effect=update_side_effect) @@ -38,18 +42,21 @@ def reset_side_effect(): @pytest.fixture(name="mock_source_pair") -def fixture_mock_source_pair(mock_source): +def fixture_mock_source_pair(mock_source: Mock) -> tuple[Mock, Mock]: """Create a pair of mock sources.""" source2 = Mock(spec=Source) source2.sacc_tracer = "tracer_2" source2.is_updated = Mock(return_value=False) - def update_side_effect2(_params, _updated_record=None): + def update_side_effect2( + _params: ParamsMap, + _updated_record: list[UpdatableUsageRecord] | None = None, + ) -> None: if source2.is_updated(): return source2.is_updated.return_value = True - def reset_side_effect2(): + def reset_side_effect2() -> None: source2.is_updated.return_value = False source2.update = Mock(side_effect=update_side_effect2) @@ -57,32 +64,7 @@ def reset_side_effect2(): return (mock_source, source2) -class FakeSource(Source): - """A minimal concrete Source implementation for testing.""" - - def read_systematics(self, sacc_data: sacc.Sacc) -> None: - pass - - def _read(self, sacc_data: sacc.Sacc) -> None: - pass - - def _update_source(self, params: ParamsMap) -> None: - pass - - def get_scale(self) -> float: - return 1.0 - - def create_tracers(self, tools: ModelingTools): - return [] - - -@pytest.fixture -def source_pair(): - """Create a pair of concrete FakeSource instances.""" - return (FakeSource("tracer_1"), FakeSource("tracer_2")) - - -def test_two_point_theory_initialization(): +def test_two_point_theory_initialization() -> None: """Test basic TwoPointTheory initialization.""" source1 = Mock() source2 = Mock() @@ -100,7 +82,7 @@ def test_two_point_theory_initialization(): assert not theory.cells -def test_two_point_theory_with_custom_interp_ells(): +def test_two_point_theory_with_custom_interp_ells() -> None: """Test TwoPointTheory with custom interpolation ells generator.""" source1 = Mock() source2 = Mock() @@ -117,7 +99,7 @@ def test_two_point_theory_with_custom_interp_ells(): assert len(theory.ells_for_xi) > 0 -def test_two_point_theory_with_ell_or_theta_config(): +def test_two_point_theory_with_ell_or_theta_config() -> None: """Test TwoPointTheory initialization with ell_or_theta configuration.""" source1 = Mock() source2 = Mock() @@ -134,7 +116,7 @@ def test_two_point_theory_with_ell_or_theta_config(): assert theory.ell_or_theta_config == ell_or_theta -def test_two_point_theory_with_tracers(): +def test_two_point_theory_with_tracers() -> None: """Test TwoPointTheory initialization with explicit tracer names.""" source1 = Mock() source2 = Mock() @@ -149,7 +131,7 @@ def test_two_point_theory_with_tracers(): assert theory.sacc_tracers == tracers -def test_two_point_theory_with_int_options(): +def test_two_point_theory_with_int_options() -> None: """Test TwoPointTheory with integration options.""" source1 = Mock() source2 = Mock() @@ -167,7 +149,7 @@ def test_two_point_theory_with_int_options(): assert theory.int_options == int_options -def test_two_point_theory_with_apply_interp(): +def test_two_point_theory_with_apply_interp() -> None: """Test TwoPointTheory with custom apply_interp flag.""" source1 = Mock() source2 = Mock() @@ -181,7 +163,7 @@ def test_two_point_theory_with_apply_interp(): assert theory.apply_interp == ApplyInterpolationWhen.ALL -def test_two_point_theory_source0_property(mock_source_pair): +def test_two_point_theory_source0_property(mock_source_pair: tuple[Mock, Mock]) -> None: """Test source0 property returns first source.""" theory = TwoPointTheory( sacc_data_type="galaxy_density_cl", @@ -191,7 +173,7 @@ def test_two_point_theory_source0_property(mock_source_pair): assert theory.source0 == mock_source_pair[0] -def test_two_point_theory_source1_property(mock_source_pair): +def test_two_point_theory_source1_property(mock_source_pair: tuple[Mock, Mock]) -> None: """Test source1 property returns second source.""" theory = TwoPointTheory( sacc_data_type="galaxy_density_cl", @@ -201,7 +183,7 @@ def test_two_point_theory_source1_property(mock_source_pair): assert theory.source1 == mock_source_pair[1] -def test_two_point_theory_update(mock_source_pair): +def test_two_point_theory_update(mock_source_pair: tuple[Mock, Mock]) -> None: """Test update method calls update on sources.""" theory = TwoPointTheory( sacc_data_type="galaxy_density_cl", @@ -216,7 +198,7 @@ def test_two_point_theory_update(mock_source_pair): assert mock_source_pair[1].is_updated() -def test_two_point_theory_reset(mock_source_pair): +def test_two_point_theory_reset(mock_source_pair: tuple[Mock, Mock]) -> None: """Test reset method calls reset on sources.""" theory = TwoPointTheory( sacc_data_type="galaxy_density_cl", @@ -236,7 +218,7 @@ def test_two_point_theory_reset(mock_source_pair): assert not mock_source_pair[1].is_updated() -def test_two_point_theory_initialize_sources_different_sources(): +def test_two_point_theory_initialize_sources_different_sources() -> None: """Test initialize_sources with two different sources.""" source1 = Mock() source1.sacc_tracer = "tracer_1" @@ -256,7 +238,7 @@ def test_two_point_theory_initialize_sources_different_sources(): assert theory.sacc_tracers == TracerNames("tracer_1", "tracer_2") -def test_two_point_theory_initialize_sources_same_source(): +def test_two_point_theory_initialize_sources_same_source() -> None: """Test initialize_sources with the same source for both.""" source = Mock() source.sacc_tracer = "tracer_1" @@ -274,7 +256,7 @@ def test_two_point_theory_initialize_sources_same_source(): assert theory.sacc_tracers == TracerNames("tracer_1", "tracer_1") -def test_two_point_theory_get_tracers_and_scales_different_sources(): +def test_two_point_theory_get_tracers_and_scales_different_sources() -> None: """Test get_tracers_and_scales with different sources.""" source1 = Mock() source1_tracers = [Mock(), Mock()] @@ -300,7 +282,7 @@ def test_two_point_theory_get_tracers_and_scales_different_sources(): assert scale1 == 2.0 -def test_two_point_theory_get_tracers_and_scales_same_source(): +def test_two_point_theory_get_tracers_and_scales_same_source() -> None: """Test get_tracers_and_scales with the same source.""" source = Mock() source_tracers = [Mock(), Mock()] @@ -325,7 +307,7 @@ def test_two_point_theory_get_tracers_and_scales_same_source(): assert scale1 == 1.5 -def test_two_point_theory_generate_ells_for_interpolation(): +def test_two_point_theory_generate_ells_for_interpolation() -> None: """Test generate_ells_for_interpolation method.""" source1 = Mock() source2 = Mock() diff --git a/tests/test_base_likelihood.py b/tests/test_base_likelihood.py index 7ecfa11e8..664eaec28 100644 --- a/tests/test_base_likelihood.py +++ b/tests/test_base_likelihood.py @@ -3,24 +3,27 @@ """ import numpy as np +import numpy.typing as npt import pytest +import sacc import firecrown.likelihood._likelihood as lk +from firecrown.modeling_tools import ModelingTools class MinimalLikelihood(lk.Likelihood): """A likelihood implementing every method of the base class trivially.""" - def compute_loglike(self, _): + def compute_loglike(self, _: ModelingTools) -> float: return -1.0 - def read(self, _): + def read(self, _: sacc.Sacc) -> None: pass - def make_realization_vector(self): + def make_realization_vector(self) -> npt.NDArray[np.float64]: return np.zeros(0) -def test_unimplemented_make_realization_vector(): +def test_unimplemented_make_realization_vector() -> None: with pytest.raises(NotImplementedError): lk.Likelihood.make_realization_vector(MinimalLikelihood()) diff --git a/tests/test_bug_413.py b/tests/test_bug_413.py index 4c92a60e3..40c44b724 100644 --- a/tests/test_bug_413.py +++ b/tests/test_bug_413.py @@ -2,6 +2,8 @@ from typing import Tuple +import numpy as np +import numpy.typing as npt import pyccl import sacc @@ -76,7 +78,14 @@ def make_twopoint_with_optional_systematics( return a, b, statistic, tools -def test_calculate_pk(sacc_galaxy_xis_lens0_lens1): +def test_calculate_pk( + sacc_galaxy_xis_lens0_lens1: tuple[ + sacc.Sacc, + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.float64], + ], +) -> None: # Set up a TwoPoint object in which the first source has a PT systematic # and the second source doe not have a PT systematic. diff --git a/tests/test_ccl_factory_deprecated.py b/tests/test_ccl_factory_deprecated.py index 6b5e24a4d..fd2c29610 100644 --- a/tests/test_ccl_factory_deprecated.py +++ b/tests/test_ccl_factory_deprecated.py @@ -14,7 +14,7 @@ import pytest -def test_ccl_factory_deprecation_warning(): +def test_ccl_factory_deprecation_warning() -> None: """Test that importing ccl_factory emits DeprecationWarning. This must be the first test that imports the module, as subsequent imports @@ -34,7 +34,7 @@ def test_ccl_factory_deprecation_warning(): assert firecrown.ccl_factory is not None # Verify import succeeded -def test_ccl_factory_deprecation_warning_content(): +def test_ccl_factory_deprecation_warning_content() -> None: """Test the deprecation warning mentions modeling_tools. This test relies on module caching, so it won't see the warning. @@ -48,7 +48,7 @@ def test_ccl_factory_deprecation_warning_content(): # If we get here without error, the import worked -def test_all_items_importable(): +def test_all_items_importable() -> None: """Test that all __all__ items can be imported from deprecated package.""" # pylint: disable=import-outside-toplevel from firecrown.ccl_factory import ( @@ -81,7 +81,7 @@ def test_all_items_importable(): @pytest.mark.filterwarnings( r"ignore:firecrown\.ccl_factory is deprecated.*:DeprecationWarning" ) -def test_items_identical_to_new_location(): +def test_items_identical_to_new_location() -> None: """Test that imported items are the same objects as in modeling_tools.""" # pylint: disable=import-outside-toplevel # Import from both locations @@ -141,7 +141,7 @@ def test_items_identical_to_new_location(): assert OldPowerSpec is PowerSpec -def test_module_import_as_alias(): +def test_module_import_as_alias() -> None: """Test importing the module with an alias.""" # pylint: disable=import-outside-toplevel import firecrown.ccl_factory as fac @@ -152,7 +152,7 @@ def test_module_import_as_alias(): assert hasattr(fac, "CCLCreationMode") -def test_ccl_factory_all_list(): +def test_ccl_factory_all_list() -> None: """Test that __all__ is preserved in deprecated module.""" # pylint: disable=import-outside-toplevel import firecrown.ccl_factory @@ -173,7 +173,7 @@ def test_ccl_factory_all_list(): assert set(firecrown.ccl_factory.__all__) == set(expected_all) -def test_ccl_factory_functional(): +def test_ccl_factory_functional() -> None: """Test that CCLFactory from deprecated module is functional.""" # pylint: disable=import-outside-toplevel from firecrown.ccl_factory import CCLFactory, PoweSpecAmplitudeParameter diff --git a/tests/test_cmb_factories.py b/tests/test_cmb_factories.py index ddb500b2c..744241000 100644 --- a/tests/test_cmb_factories.py +++ b/tests/test_cmb_factories.py @@ -15,7 +15,7 @@ from firecrown.updatable import ParamsMap -def test_cmb_convergence_args(): +def test_cmb_convergence_args() -> None: """Test CMBConvergenceArgs creation and properties.""" args = CMBConvergenceArgs(scale=2.0, field="delta_matter", z_source=1090.0) @@ -24,7 +24,7 @@ def test_cmb_convergence_args(): assert args.z_source == 1090.0 -def test_cmb_convergence_init(): +def test_cmb_convergence_init() -> None: """Test CMBConvergence initialization.""" cmb_conv = CMBConvergence(sacc_tracer="cmb", scale=1.5, z_source=1090.0) @@ -34,7 +34,7 @@ def test_cmb_convergence_init(): assert cmb_conv.current_tracer_args is None -def test_cmb_convergence_create_ready(): +def test_cmb_convergence_create_ready() -> None: """Test CMBConvergence.create_ready method.""" cmb_conv = CMBConvergence.create_ready( sacc_tracer="cmb", scale=2.0, z_source=1090.0 @@ -48,7 +48,7 @@ def test_cmb_convergence_create_ready(): assert cmb_conv.tracer_args.z_source == 1090.0 -def test_cmb_convergence_read_systematics(): +def test_cmb_convergence_read_systematics() -> None: """Test that read_systematics does nothing for CMB sources.""" cmb_conv = CMBConvergence(sacc_tracer="cmb") @@ -59,9 +59,9 @@ def test_cmb_convergence_read_systematics(): cmb_conv.read_systematics(mock_sacc) -def test_cmb_convergence_read(sacc_galaxy_cells_src0_src0): +def test_cmb_convergence_read() -> None: """Test CMBConvergence _read method.""" - sacc_data, _, _ = sacc_galaxy_cells_src0_src0 + sacc_data = sacc.Sacc() cmb_conv = CMBConvergence.create_ready(sacc_tracer="cmb", scale=1.0) @@ -72,7 +72,7 @@ def test_cmb_convergence_read(sacc_galaxy_cells_src0_src0): assert cmb_conv.tracer_args.field == "delta_matter" -def test_cmb_convergence_read_missing_tracer_args(): +def test_cmb_convergence_read_missing_tracer_args() -> None: """Test that _read properly initializes tracer_args.""" cmb_conv = CMBConvergence(sacc_tracer="cmb", scale=2.0, z_source=1090.0) @@ -92,7 +92,9 @@ def test_cmb_convergence_read_missing_tracer_args(): assert cmb_conv.tracer_args.field == "delta_matter" -def test_cmb_convergence_create_tracers(tools_with_vanilla_cosmology: ModelingTools): +def test_cmb_convergence_create_tracers( + tools_with_vanilla_cosmology: ModelingTools, +) -> None: """Test CMBConvergence create_tracers method.""" cmb_conv = CMBConvergence.create_ready( sacc_tracer="cmb", scale=1.0, z_source=1100.0 @@ -108,7 +110,7 @@ def test_cmb_convergence_create_tracers(tools_with_vanilla_cosmology: ModelingTo assert cmb_conv.current_tracer_args == tracer_args -def test_cmb_convergence_get_scale(): +def test_cmb_convergence_get_scale() -> None: """Test CMBConvergence get_scale method.""" cmb_conv = CMBConvergence.create_ready(sacc_tracer="cmb", scale=2.5) @@ -122,7 +124,7 @@ def test_cmb_convergence_get_scale(): assert cmb_conv.get_scale() == 2.5 -def test_cmb_convergence_factory_init(): +def test_cmb_convergence_factory_init() -> None: """Test CMBConvergenceFactory initialization.""" factory = CMBConvergenceFactory(z_source=1090.0, scale=2.0) @@ -131,7 +133,7 @@ def test_cmb_convergence_factory_init(): assert not factory._cache # pylint: disable=protected-access -def test_cmb_convergence_factory_create(): +def test_cmb_convergence_factory_create() -> None: """Test CMBConvergenceFactory create method.""" factory = CMBConvergenceFactory(z_source=1090.0, scale=1.5) @@ -149,7 +151,7 @@ def test_cmb_convergence_factory_create(): assert cmb_conv.z_source == 1090.0 -def test_cmb_convergence_factory_create_caching(): +def test_cmb_convergence_factory_create_caching() -> None: """Test that CMBConvergenceFactory caches created objects.""" factory = CMBConvergenceFactory() @@ -166,7 +168,7 @@ def test_cmb_convergence_factory_create_caching(): assert cmb_conv1 is cmb_conv2 -def test_cmb_convergence_factory_create_from_metadata_only(): +def test_cmb_convergence_factory_create_from_metadata_only() -> None: """Test CMBConvergenceFactory create_from_metadata_only method.""" factory = CMBConvergenceFactory(z_source=1100.0, scale=1.0) @@ -177,7 +179,7 @@ def test_cmb_convergence_factory_create_from_metadata_only(): assert cmb_conv.z_source == 1100.0 -def test_cmb_convergence_factory_create_from_metadata_caching(): +def test_cmb_convergence_factory_create_from_metadata_caching() -> None: """Test that create_from_metadata_only also uses caching.""" factory = CMBConvergenceFactory() @@ -188,7 +190,7 @@ def test_cmb_convergence_factory_create_from_metadata_caching(): assert cmb_conv1 is cmb_conv2 -def test_cmb_convergence_no_systematics(): +def test_cmb_convergence_no_systematics() -> None: """Test that CMB sources have no systematics.""" cmb_conv = CMBConvergence(sacc_tracer="cmb") @@ -196,7 +198,7 @@ def test_cmb_convergence_no_systematics(): assert not hasattr(cmb_conv, "systematics") -def test_cmb_convergence_update_params(): +def test_cmb_convergence_update_params() -> None: """Test that CMBConvergence can handle parameter updates.""" cmb_conv = CMBConvergence.create_ready(sacc_tracer="cmb") @@ -206,7 +208,7 @@ def test_cmb_convergence_update_params(): cmb_conv.update(params) -def test_cmb_convergence_args_immutable(): +def test_cmb_convergence_args_immutable() -> None: """Test that CMBConvergenceArgs is immutable.""" args = CMBConvergenceArgs(scale=1.0) @@ -215,7 +217,7 @@ def test_cmb_convergence_args_immutable(): args.scale = 2.0 # type: ignore[misc] -def test_cmb_convergence_factory_different_params(): +def test_cmb_convergence_factory_different_params() -> None: """Test factory with different parameters creates different objects.""" factory1 = CMBConvergenceFactory(z_source=1090.0, scale=1.0) factory2 = CMBConvergenceFactory(z_source=1100.0, scale=2.0) @@ -238,7 +240,7 @@ def test_cmb_convergence_factory_different_params(): @pytest.mark.parametrize("z_source", [1090.0, 1100.0, 1110.0]) def test_cmb_convergence_different_z_source( z_source: float, tools_with_vanilla_cosmology: ModelingTools -): +) -> None: """Test CMBConvergence with different z_source values.""" cmb_conv = CMBConvergence.create_ready(sacc_tracer="cmb", z_source=z_source) @@ -249,7 +251,7 @@ def test_cmb_convergence_different_z_source( @pytest.mark.parametrize("scale", [0.5, 1.0, 1.5, 2.0]) -def test_cmb_convergence_different_scales(scale: float): +def test_cmb_convergence_different_scales(scale: float) -> None: """Test CMBConvergence with different scale values.""" cmb_conv = CMBConvergence.create_ready(sacc_tracer="cmb", scale=scale) diff --git a/tests/test_data_functions.py b/tests/test_data_functions.py index 04c0c4ac2..0131dbbdd 100644 --- a/tests/test_data_functions.py +++ b/tests/test_data_functions.py @@ -23,8 +23,12 @@ ], ) def test_ensure_no_overlaps( - measurement, index_set, index_sets, other_measurements, expected_result -): + measurement: str, + index_set: set[int], + index_sets: list[set[int]], + other_measurements: list[str], + expected_result: type[ValueError] | None, +) -> None: if expected_result is None: ensure_no_overlaps(measurement, index_set, index_sets, other_measurements) else: @@ -34,12 +38,12 @@ def test_ensure_no_overlaps( # Additional test for empty other_measurements and index_sets -def test_empty_other_measurements_and_index_sets(): +def test_empty_other_measurements_and_index_sets() -> None: ensure_no_overlaps("A", {1, 2}, [], []) # Additional test for overlap with the last set in the list -def test_overlap_with_last_set(): +def test_overlap_with_last_set() -> None: with pytest.raises(ValueError) as excinfo: ensure_no_overlaps("A", {1}, [{3, 4}, {5, 6}, {1, 7}], ["B", "C", "D"]) assert "overlap" in str(excinfo.value), "Error message should contain 'overlap'" @@ -47,10 +51,10 @@ def test_overlap_with_last_set(): # Test for edge case where the index_set is empty but there are non-empty set # in index_sets -def test_empty_index_set_with_non_empty_index_sets(): +def test_empty_index_set_with_non_empty_index_sets() -> None: ensure_no_overlaps("A", set(), [{1}, {2}], ["B", "C"]) # Test for edge case where all sets are empty -def test_all_sets_empty(): +def test_all_sets_empty() -> None: ensure_no_overlaps("A", set(), [set(), set()], ["B", "C"]) diff --git a/tests/test_descriptors.py b/tests/test_descriptors.py index d7025246d..802d42f83 100644 --- a/tests/test_descriptors.py +++ b/tests/test_descriptors.py @@ -18,7 +18,7 @@ class HasUOF: x = TypeFloat(allow_none=True) - def __init__(self): + def __init__(self) -> None: """Initialize a HasUOF object with default value `self.x=None`.""" self.x = None @@ -28,7 +28,7 @@ class HasCOF: x = TypeFloat(1.0, 3.0, allow_none=True) - def __init__(self): + def __init__(self) -> None: """Initialize a HasCOF object with default value of `self.x=None`.""" self.x = None @@ -38,7 +38,7 @@ class HasURF: x = TypeFloat() - def __init__(self): + def __init__(self) -> None: """Initialize a HasURF object with default value `self.x=0.0`.""" self.x = 0.0 @@ -48,7 +48,7 @@ class HasCRF: x = TypeFloat(1.0, 3.0) - def __init__(self): + def __init__(self) -> None: """Initialize a HasCRF with the default value `self.x=2.0`.""" self.x = 2.0 @@ -58,7 +58,7 @@ class HasLowerBound: x = TypeFloat(minvalue=0.0) - def __init__(self): + def __init__(self) -> None: """Initialize a HasLowerBound object with the default value `self.x=0.0`.""" self.x = 0.0 @@ -69,7 +69,7 @@ class HasUpperBound: x = TypeFloat(maxvalue=1.0) - def __init__(self): + def __init__(self) -> None: """Initialize a HasUpperBound object with the default value `self.x=1.0`.""" self.x = 1.0 @@ -78,7 +78,8 @@ def __init__(self): class NotStringy: """A class that can not be turned into a string.""" - def __str__(self): + # This negative-test double deliberately violates object.__str__'s str contract. + def __str__(self) -> None: # type: ignore[override] """Does not return a string""" @@ -112,7 +113,7 @@ class StartsWithCow: x = TypeString(predicate=lambda s: s.startswith("cow")) -def test_unconstrained_optional_float(): +def test_unconstrained_optional_float() -> None: d = HasUOF() with pytest.raises(TypeError): d.x = 3 # not a literal float @@ -128,7 +129,7 @@ def test_unconstrained_optional_float(): assert d.x == math.inf -def test_constrained_optional_float(): +def test_constrained_optional_float() -> None: d = HasCOF() with pytest.raises(TypeError): d.x = 3 # not a literal float @@ -149,7 +150,7 @@ def test_constrained_optional_float(): assert d.x == 3.0 -def test_unconstrained_required_float(): +def test_unconstrained_required_float() -> None: d = HasURF() with pytest.raises(TypeError): d.x = 3 # not a literal float @@ -163,7 +164,7 @@ def test_unconstrained_required_float(): assert d.x == math.inf -def test_constrained_required_float(): +def test_constrained_required_float() -> None: d = HasCRF() with pytest.raises(TypeError): d.x = 3 # not a literal float @@ -184,7 +185,7 @@ def test_constrained_required_float(): assert d.x == 3.0 -def test_lower_bound_float(): +def test_lower_bound_float() -> None: d = HasLowerBound() assert d.x == 0.0 @@ -203,7 +204,7 @@ def test_lower_bound_float(): assert d.x == math.inf -def test_upper_bound_float(): +def test_upper_bound_float() -> None: d = HasUpperBound() assert d.x == 1.0 @@ -218,14 +219,14 @@ def test_upper_bound_float(): assert d.x == -math.inf -def test_reading_string(): +def test_reading_string() -> None: d = HasString() d.x = "cow" assert d.x == "cow" -def test_string_conversion_failure(): +def test_string_conversion_failure() -> None: d = HasString() with pytest.raises(TypeError): # We ignore type checking on this line because we are testing the error @@ -233,21 +234,21 @@ def test_string_conversion_failure(): d.x = NotStringy() # type: ignore -def test_string_too_short(): +def test_string_too_short() -> None: d = HasShortBound() d.x = "walrus" with pytest.raises(ValueError): d.x = "cow" -def test_string_too_long(): +def test_string_too_long() -> None: d = HasLongBound() d.x = "ou" with pytest.raises(ValueError): d.x = "cow" -def test_string_predicate(): +def test_string_predicate() -> None: d = StartsWithCow() d.x = "cowabunga" with pytest.raises(ValueError): @@ -255,7 +256,7 @@ def test_string_predicate(): # Tests for _base module -def test_base_module_exports_math(): +def test_base_module_exports_math() -> None: """Test that _base module exports math.""" # Import the module to ensure coverage # pylint: disable=import-outside-toplevel @@ -267,7 +268,7 @@ def test_base_module_exports_math(): assert "math" in base_module.__all__ -def test_base_module_exports_callable(): +def test_base_module_exports_callable() -> None: """Test that _base module exports Callable.""" # Import the module to ensure coverage # pylint: disable=import-outside-toplevel diff --git a/tests/test_fctools_refactor.py b/tests/test_fctools_refactor.py index 13a99ac37..0ffc82c60 100644 --- a/tests/test_fctools_refactor.py +++ b/tests/test_fctools_refactor.py @@ -7,6 +7,7 @@ import contextlib import io import json +from pathlib import Path from rich.console import Console @@ -14,7 +15,7 @@ from firecrown.fctools import print_code as pc -def test_parse_duration_line(tmp_path): +def test_parse_duration_line(tmp_path: Path) -> None: # private helper is nested; test via the public parse_timing_data API console = Console() tmp = tmp_path / "tmp_test_durations.txt" @@ -28,7 +29,7 @@ def test_parse_duration_line(tmp_path): assert timings["tests/test_mod.py::test_func"] == 0.12 -def test_load_json_timing(tmp_path): +def test_load_json_timing(tmp_path: Path) -> None: console = Console() data = {"tests": [{"nodeid": "tests/t.py::test_a", "duration": 0.5}]} p = tmp_path / "t.json" @@ -46,7 +47,7 @@ class Dummy: A = 1 -def test_build_class_code(): +def test_build_class_code() -> None: # Capture stdout of the public display function and assert the content buf = io.StringIO() with contextlib.redirect_stdout(buf): diff --git a/tests/test_hm_systematics.py b/tests/test_hm_systematics.py index 92f7392e4..7a9575645 100644 --- a/tests/test_hm_systematics.py +++ b/tests/test_hm_systematics.py @@ -41,7 +41,9 @@ def fixture_sacc_data() -> sacc.Sacc: @pytest.mark.slow -def test_hm_systematics(weak_lensing_source, sacc_data): +def test_hm_systematics( + weak_lensing_source: wl.WeakLensing, sacc_data: sacc.Sacc +) -> None: # The following disabling of pylint warnings are TEMPORARY. Disabling warnings is # generally not a good practice. In this case, the warnings are indicating that this # test is too complicated. diff --git a/tests/test_likelihood_factories_two_point.py b/tests/test_likelihood_factories_two_point.py index 6efeb5ea3..6d9a2c709 100644 --- a/tests/test_likelihood_factories_two_point.py +++ b/tests/test_likelihood_factories_two_point.py @@ -7,7 +7,7 @@ """ -def test_two_point_factory_importable(): +def test_two_point_factory_importable() -> None: """Verify TwoPointFactory is importable from firecrown.likelihood.factories.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood.factories import TwoPointFactory @@ -15,7 +15,7 @@ def test_two_point_factory_importable(): assert TwoPointFactory is not None -def test_two_point_factory_identical_to_likelihood(): +def test_two_point_factory_identical_to_likelihood() -> None: """Verify TwoPointFactory from factories is identical to firecrown.likelihood.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood import TwoPointFactory @@ -24,7 +24,7 @@ def test_two_point_factory_identical_to_likelihood(): assert FactoriesTWPF is TwoPointFactory -def test_two_point_factory_in_all(): +def test_two_point_factory_in_all() -> None: """Verify TwoPointFactory is listed in __all__ of firecrown.likelihood.factories.""" # pylint: disable=import-outside-toplevel import firecrown.likelihood.factories as factories_module @@ -32,7 +32,7 @@ def test_two_point_factory_in_all(): assert "TwoPointFactory" in factories_module.__all__ -def test_two_point_factory_is_pydantic_model(): +def test_two_point_factory_is_pydantic_model() -> None: """Verify TwoPointFactory is a Pydantic BaseModel subclass.""" # pylint: disable=import-outside-toplevel from pydantic import BaseModel @@ -42,7 +42,7 @@ def test_two_point_factory_is_pydantic_model(): assert issubclass(TwoPointFactory, BaseModel) -def test_two_point_factory_has_correlation_space_field(): +def test_two_point_factory_has_correlation_space_field() -> None: """Verify TwoPointFactory exposes the correlation_space field.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood.factories import TwoPointFactory diff --git a/tests/test_likelihood_gaussian_deprecated.py b/tests/test_likelihood_gaussian_deprecated.py index e6fc46b2d..9ee52e62c 100644 --- a/tests/test_likelihood_gaussian_deprecated.py +++ b/tests/test_likelihood_gaussian_deprecated.py @@ -14,7 +14,7 @@ import pytest -def test_gaussian_deprecation_warning(): +def test_gaussian_deprecation_warning() -> None: """Importing firecrown.likelihood.gaussian emits a DeprecationWarning. This must be the first test that imports the module, as subsequent imports @@ -31,7 +31,7 @@ def test_gaussian_deprecation_warning(): import firecrown.likelihood.gaussian # noqa: F401 -def test_const_gaussian_importable(): +def test_const_gaussian_importable() -> None: """Verify ConstGaussian is importable from the deprecated module.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood.gaussian import ConstGaussian @@ -39,7 +39,7 @@ def test_const_gaussian_importable(): assert ConstGaussian is not None -def test_const_gaussian_identical_to_new_location(): +def test_const_gaussian_identical_to_new_location() -> None: """Verify ConstGaussian from deprecated module is the same as from _gaussian.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood._gaussian import ConstGaussian @@ -48,7 +48,7 @@ def test_const_gaussian_identical_to_new_location(): assert OldConstGaussian is ConstGaussian -def test_const_gaussian_in_all(): +def test_const_gaussian_in_all() -> None: """Verify ConstGaussian is listed in __all__ of the deprecated module.""" # pylint: disable=import-outside-toplevel import firecrown.likelihood.gaussian as gaussian_module diff --git a/tests/test_modeling_tools.py b/tests/test_modeling_tools.py index f67fff4cd..eb5743352 100644 --- a/tests/test_modeling_tools.py +++ b/tests/test_modeling_tools.py @@ -151,7 +151,7 @@ def test_modeling_tools_get_cM_relation_with_setting() -> None: class DummyPowerspectrumModifier(PowerspectrumModifier): """Dummy power spectrum modifier for testing.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self.name = "dummy:dummy" diff --git a/tests/test_modeling_tools_ccl_factory.py b/tests/test_modeling_tools_ccl_factory.py index 0e58893f3..af276c416 100644 --- a/tests/test_modeling_tools_ccl_factory.py +++ b/tests/test_modeling_tools_ccl_factory.py @@ -2,6 +2,7 @@ import itertools as it import re +from typing import cast import numpy as np import pyccl @@ -29,12 +30,14 @@ @pytest.fixture(name="amplitude_parameter", params=list(PoweSpecAmplitudeParameter)) -def fixture_amplitude_parameter(request): +def fixture_amplitude_parameter( + request: pytest.FixtureRequest, +) -> PoweSpecAmplitudeParameter: return request.param @pytest.fixture(name="neutrino_mass_splits", params=list(NeutrinoMassSplits)) -def fixture_neutrino_mass_splits(request): +def fixture_neutrino_mass_splits(request: pytest.FixtureRequest) -> NeutrinoMassSplits: return request.param @@ -43,7 +46,7 @@ def fixture_neutrino_mass_splits(request): params=[True, False], ids=["require_nonlinear_pk", "no_require_nonlinear_pk"], ) -def fixture_require_nonlinear_pk(request): +def fixture_require_nonlinear_pk(request: pytest.FixtureRequest) -> bool: return request.param @@ -55,7 +58,7 @@ def fixture_require_nonlinear_pk(request): CCLCreationMode.MU_SIGMA_ISITGR, ], ) -def fixture_ccl_creation_mode(request) -> CCLCreationMode: +def fixture_ccl_creation_mode(request: pytest.FixtureRequest) -> CCLCreationMode: """Fixture providing each CCLCreationMode.""" return request.param @@ -71,7 +74,9 @@ def fixture_ccl_creation_mode(request) -> CCLCreationMode: ], ids=["no_camb", "mead", "mead_kmax", "mead_kmax_lmax", "ppf"], ) -def fixture_camb_extra_params(request) -> CAMBExtraParams | None: +def fixture_camb_extra_params( + request: pytest.FixtureRequest, +) -> CAMBExtraParams | None: """Fixture providing each possible CAMBExtraParams configuration.""" if request.param is not None: return CAMBExtraParams.model_validate(request.param) @@ -109,7 +114,9 @@ def fixture_camb_extra_params(request) -> CAMBExtraParams | None: ], ids=lambda x: x[2], # Use the third element (id string) as the test id ) -def fixture_ccl_creation_mode_and_camb_params(request): +def fixture_ccl_creation_mode_and_camb_params( + request: pytest.FixtureRequest, +) -> tuple[CCLCreationMode, CAMBExtraParams | None]: """Provides valid combinations of creation mode and CAMB params. CAMB extra parameters are only compatible with PURE_CCL_MODE, so this @@ -132,7 +139,9 @@ def fixture_ccl_creation_mode_and_camb_params(request): params=[None, {"a_spline_na": 451}], ids=["default", "set_a_spline_na"], ) -def fixture_ccl_spline_params(request) -> CCLSplineParams | None: +def fixture_ccl_spline_params( + request: pytest.FixtureRequest, +) -> CCLSplineParams | None: return CCLSplineParams(**request.param) if request.param is not None else None @@ -173,7 +182,7 @@ def fixture_ccl_spline_params(request) -> CCLSplineParams | None: params=[BACKGROUND, BACKGROUND | PK_LINEAR, BACKGROUND | PK_LINEAR | PK_NONLIN], ids=["background", "background_linear_pk", "background_linear_pk_nonlinear_pk"], ) -def fixture_calculator_args(request) -> CCLCalculatorArgs: +def fixture_calculator_args(request: pytest.FixtureRequest) -> CCLCalculatorArgs: return request.param @@ -202,7 +211,9 @@ def fixture_calculator_args(request) -> CCLCalculatorArgs: ], ids=lambda x: x[2], # Use the third element (id string) as the test id ) -def fixture_require_nonlinear_pk_and_calculator_args(request): +def fixture_require_nonlinear_pk_and_calculator_args( + request: pytest.FixtureRequest, +) -> tuple[bool, CCLCalculatorArgs]: """Fixture providing valid combinations of require_nonlinear_pk and calculator_args. Nonlinear PK requires linear PK to be present, so this fixture only generates @@ -219,7 +230,9 @@ def fixture_require_nonlinear_pk_and_calculator_args(request): "transfer_function", list(CCLPureModeTransferFunction), ) -def test_ccl_factory_pure_mode_transfer_function(transfer_function): +def test_ccl_factory_pure_mode_transfer_function( + transfer_function: CCLPureModeTransferFunction, +) -> None: ccl_factory = CCLFactory( creation_mode=CCLCreationMode.PURE_CCL_MODE, pure_ccl_transfer_function=transfer_function, @@ -233,7 +246,7 @@ def test_ccl_factory_pure_mode_transfer_function(transfer_function): assert isinstance(cosmo, pyccl.Cosmology) -def test_ccl_factory_invalid_pure_mode_transfer_function(): +def test_ccl_factory_invalid_pure_mode_transfer_function() -> None: with pytest.raises(ValueError, match="is not a valid CCLPureModeTransferFunction"): CCLFactory( creation_mode=CCLCreationMode.PURE_CCL_MODE, @@ -249,7 +262,7 @@ def test_setting_each_spline_param( # param_value to be tested. ccl_creation_mode, camb_extra_params = ccl_creation_mode_and_camb_params - def test_helper(param_name: str, param_value: float | int): + def test_helper(param_name: str, param_value: float | int) -> None: # Using combined fixture ensures only valid combinations are tested original_param_value = getattr(pyccl.spline_params, param_name.upper()) args = {param_name: param_value} @@ -302,7 +315,7 @@ def test_helper(param_name: str, param_value: float | int): test_helper("n_ell_corr", 4444) -def test_ccl_spline_params_validation(): +def test_ccl_spline_params_validation() -> None: with pytest.raises(pydantic.ValidationError): _ = CCLSplineParams(logm_spline_min=20, logm_spline_max=10) with pytest.raises(pydantic.ValidationError): @@ -311,7 +324,7 @@ def test_ccl_spline_params_validation(): _ = CCLSplineParams(ell_min_corr=100, ell_max_corr=10) -def test_ccl_spline_params_context_manager_exception(): +def test_ccl_spline_params_context_manager_exception() -> None: with CCLSplineParams(k_max=900) as params: assert params.k_max == 900 with pytest.raises(AssertionError): @@ -319,7 +332,7 @@ def test_ccl_spline_params_context_manager_exception(): assert False -def _nu_mass_is_list(mass_split: NeutrinoMassSplits): +def _nu_mass_is_list(mass_split: NeutrinoMassSplits) -> bool: """Check if the neutrino mass split is a list. This function is used to check if the neutrino mass split is a list or a sum. @@ -845,7 +858,7 @@ def test_ccl_factory_invalid_extra_params() -> None: CCLFactory(not_a_valid_param="Im not a valid value") -def test_validate_creation_mode_incompatible(): +def test_validate_creation_mode_incompatible() -> None: ccl_factory = CCLFactory(creation_mode=CCLCreationMode.PURE_CCL_MODE) params = get_default_params_map(ccl_factory) ccl_factory.update(params) @@ -1171,7 +1184,11 @@ def test_camb_extra_params_set_hm_parameters_wrong_name() -> None: ), ], ) -def test_ccl_factory_using_camb(creation_mode, transfer_function, expected): +def test_ccl_factory_using_camb( + creation_mode: CCLCreationMode, + transfer_function: CCLPureModeTransferFunction, + expected: bool, +) -> None: factory = CCLFactory( creation_mode=creation_mode, pure_ccl_transfer_function=transfer_function, @@ -1227,8 +1244,11 @@ def test_ccl_factory_using_camb(creation_mode, transfer_function, expected): ], ) def test_ccl_factory_ccl_powerspectra( - creation_mode, transfer_function, nonlinear_pk, matter_pk_str -): + creation_mode: CCLCreationMode, + transfer_function: CCLPureModeTransferFunction, + nonlinear_pk: bool, + matter_pk_str: str, +) -> None: factory = CCLFactory( creation_mode=creation_mode, pure_ccl_transfer_function=transfer_function, @@ -1248,7 +1268,11 @@ def test_ccl_factory_ccl_powerspectra( [0.0, 0.1, 0.2], [(2.0e-9, None), (None, 0.81)], list(NeutrinoMassSplits) ), ) -def test_ccl_factory_parameters_passthrough_all(m_nu, As_sigma8, mass_split) -> None: +def test_ccl_factory_parameters_passthrough_all( + m_nu: float, + As_sigma8: tuple[float | None, float | None], + mass_split: NeutrinoMassSplits, +) -> None: """Ensure all cosmology parameters provided by the user are passed through unchanged to the created CCL cosmology when using the CCLFactory/ModelingTools plumbing. @@ -1259,7 +1283,7 @@ def test_ccl_factory_parameters_passthrough_all(m_nu, As_sigma8, mass_split) -> specifically exercised since that was the reported failing case. """ As, sigma8 = As_sigma8 - cosmo_dict = { + cosmo_dict: dict[str, float | list[float]] = { "Omega_c": 0.2906682, "Omega_b": 0.04575, "h": 0.6714, @@ -1295,7 +1319,8 @@ def test_ccl_factory_parameters_passthrough_all(m_nu, As_sigma8, mass_split) -> cosmo_dict["m_nu_2"] = 0.02 cosmo_dict["m_nu_3"] = 0.03 - tools.update(ParamsMap(cosmo_dict)) + # At this point the list form of m_nu has not yet replaced its scalar form. + tools.update(ParamsMap(cast(dict[str, float], cosmo_dict))) tools.prepare() if m_nu_is_list: diff --git a/tests/test_modeling_tools_deprecated.py b/tests/test_modeling_tools_deprecated.py index ac891f19b..1565c6d87 100644 --- a/tests/test_modeling_tools_deprecated.py +++ b/tests/test_modeling_tools_deprecated.py @@ -13,7 +13,7 @@ from firecrown.modeling_tools import ModelingTools -def test_no_deprecated_args_no_warning(): +def test_no_deprecated_args_no_warning() -> None: """Verify no DeprecationWarning is emitted without deprecated args.""" with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") @@ -22,7 +22,7 @@ def test_no_deprecated_args_no_warning(): assert len(deprecations) == 0 -def test_cluster_abundance_none_warns(): +def test_cluster_abundance_none_warns() -> None: """Passing cluster_abundance=None emits a DeprecationWarning.""" with pytest.warns( DeprecationWarning, @@ -31,7 +31,7 @@ def test_cluster_abundance_none_warns(): ModelingTools(cluster_abundance=None) -def test_cluster_deltasigma_none_warns(): +def test_cluster_deltasigma_none_warns() -> None: """Passing cluster_deltasigma=None emits a DeprecationWarning.""" with pytest.warns( DeprecationWarning, @@ -40,7 +40,7 @@ def test_cluster_deltasigma_none_warns(): ModelingTools(cluster_deltasigma=None) -def test_both_deprecated_args_warn(): +def test_both_deprecated_args_warn() -> None: """Passing both deprecated args emits two DeprecationWarnings.""" with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") @@ -52,7 +52,7 @@ def test_both_deprecated_args_warn(): assert any("cluster_deltasigma" in m for m in messages) -def test_modeling_tools_still_functional_with_deprecated_args(): +def test_modeling_tools_still_functional_with_deprecated_args() -> None: """Verify ModelingTools is functional when constructed with deprecated args.""" with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) diff --git a/tests/test_number_counts_factories.py b/tests/test_number_counts_factories.py index b12138013..df1622830 100644 --- a/tests/test_number_counts_factories.py +++ b/tests/test_number_counts_factories.py @@ -32,7 +32,7 @@ def fixture_nc_sys_factory() -> nc_factories.NumberCountsSystematicFactory: return nc_factories.PhotoZShiftFactory() -def test_photozshift_not_applied_to_photometic_measurement(): +def test_photozshift_not_applied_to_photometic_measurement() -> None: factory = nc_factories.PhotoZShiftFactory() assert factory is not None diff --git a/tests/test_number_counts_magnification_bias_systematic.py b/tests/test_number_counts_magnification_bias_systematic.py index fdb80b808..e87e650eb 100644 --- a/tests/test_number_counts_magnification_bias_systematic.py +++ b/tests/test_number_counts_magnification_bias_systematic.py @@ -11,7 +11,7 @@ import firecrown.updatable as fp -def test_creation_nc_magnification_bias_systematic(): +def test_creation_nc_magnification_bias_systematic() -> None: sys = nc_sys.MagnificationBiasSystematic(sacc_tracer="lens0") assert sys.parameter_prefix == "lens0" assert sys.eta is None @@ -23,7 +23,7 @@ def test_creation_nc_magnification_bias_systematic(): def test_update_nc_magnification_bias_systematic( tools_with_vanilla_cosmology: mt.ModelingTools, -): +) -> None: sys = nc_sys.MagnificationBiasSystematic(sacc_tracer="lens0") assert sys.parameter_prefix == "lens0" sys.update( @@ -61,7 +61,7 @@ def test_update_nc_magnification_bias_systematic( def test_update_nc_magnification_bias_systematic_with_mag_bias( tools_with_vanilla_cosmology: mt.ModelingTools, -): +) -> None: sys = nc_sys.MagnificationBiasSystematic(sacc_tracer="lens0") assert sys.parameter_prefix == "lens0" sys.update( diff --git a/tests/test_number_counts_source.py b/tests/test_number_counts_source.py index f85c2257c..42856fdf1 100644 --- a/tests/test_number_counts_source.py +++ b/tests/test_number_counts_source.py @@ -9,7 +9,7 @@ def test_get_derived_parameters( harmonic_bin_1: mt.TomographicBin, tools_with_vanilla_cosmology: mtools.ModelingTools, -): +) -> None: ncs = nc.NumberCounts.create_ready(harmonic_bin_1, derived_scale=True) ncs.update(updatable.ParamsMap({"bin_1_bias": 1.0})) ncs.create_tracers(tools_with_vanilla_cosmology) diff --git a/tests/test_parameters_deprecated.py b/tests/test_parameters_deprecated.py index 54f18ab8e..ef27076c8 100644 --- a/tests/test_parameters_deprecated.py +++ b/tests/test_parameters_deprecated.py @@ -10,7 +10,7 @@ import pytest -def test_parameters_module_emits_deprecation_warning(): +def test_parameters_module_emits_deprecation_warning() -> None: """Importing firecrown.parameters should emit a DeprecationWarning.""" # Remove the module from sys.modules if it's already imported # so we can import it fresh and catch the warning @@ -34,7 +34,7 @@ def test_parameters_module_emits_deprecation_warning(): assert "firecrown.updatable" in str(w[0].message) -def test_parameters_exports_all_names(): +def test_parameters_exports_all_names() -> None: """All expected names should be available from firecrown.parameters.""" # pylint: disable=import-outside-toplevel import firecrown.parameters as params @@ -55,7 +55,7 @@ def test_parameters_exports_all_names(): assert hasattr(params, name), f"Missing export: {name}" -def test_parameters_exports_are_same_as_updatable(): +def test_parameters_exports_are_same_as_updatable() -> None: """Exported names should be the same objects as in firecrown.updatable.""" # pylint: disable=import-outside-toplevel import firecrown.parameters as params @@ -81,7 +81,7 @@ def test_parameters_exports_are_same_as_updatable(): ), f"{name} from parameters is not the same object as from updatable" -def test_params_map_from_deprecated_module(): +def test_params_map_from_deprecated_module() -> None: """Ensure ParamsMap imported from deprecated module works correctly.""" # pylint: disable=import-outside-toplevel from firecrown.parameters import ParamsMap @@ -94,7 +94,7 @@ def test_params_map_from_deprecated_module(): @pytest.mark.filterwarnings( r"ignore:.*firecrown\.parameters module is deprecated.*:DeprecationWarning" ) -def test_required_parameters_from_deprecated_module(): +def test_required_parameters_from_deprecated_module() -> None: """Ensure RequiredParameters imported from deprecated module works correctly.""" # pylint: disable=import-outside-toplevel from firecrown.parameters import RequiredParameters, SamplerParameter @@ -109,7 +109,7 @@ def test_required_parameters_from_deprecated_module(): assert "param2" in req_params.get_params_names() -def test_derived_parameter_from_deprecated_module(): +def test_derived_parameter_from_deprecated_module() -> None: """Ensure DerivedParameter imported from deprecated module works correctly.""" # pylint: disable=import-outside-toplevel from firecrown.parameters import DerivedParameter diff --git a/tests/test_pt_systematics.py b/tests/test_pt_systematics.py index 7a1678d79..c685d4162 100644 --- a/tests/test_pt_systematics.py +++ b/tests/test_pt_systematics.py @@ -56,7 +56,11 @@ def fixture_sacc_data() -> sacc.Sacc: return sacc.Sacc.load_fits(saccfile) -def test_pt_systematics(weak_lensing_source, number_counts_source, sacc_data): +def test_pt_systematics( + weak_lensing_source: wl.WeakLensing, + number_counts_source: nc.NumberCounts, + sacc_data: sacc.Sacc, +) -> None: # The following disabling of pylint warnings are TEMPORARY. Disabling warnings is # generally not a good practice. In this case, the warnings are indicating that this # test is too complicated. @@ -253,7 +257,7 @@ def test_pt_systematics(weak_lensing_source, number_counts_source, sacc_data): assert np.allclose(cl_gg_theory, cells_gg_total, atol=0, rtol=1e-7) -def test_pt_mixed_systematics(sacc_data): +def test_pt_mixed_systematics(sacc_data: sacc.Sacc) -> None: # The following disabling of pylint warnings are TEMPORARY. Disabling warnings is # generally not a good practice. In this case, the warnings are indicating that this # test is too complicated. @@ -385,7 +389,7 @@ def test_pt_mixed_systematics(sacc_data): assert np.allclose(cl_gI, cells_gI, atol=0, rtol=1e-7) -def test_pt_mixed_systematics_zdep(sacc_data): +def test_pt_mixed_systematics_zdep(sacc_data: sacc.Sacc) -> None: # The following disabling of pylint warnings are TEMPORARY. Disabling warnings is # generally not a good practice. In this case, the warnings are indicating that this # test is too complicated. @@ -525,7 +529,11 @@ def test_pt_mixed_systematics_zdep(sacc_data): assert np.allclose(cl_gI, cells_gI, atol=0, rtol=1e-7) -def test_pt_systematics_zdep(weak_lensing_source, number_counts_source, sacc_data): +def test_pt_systematics_zdep( + weak_lensing_source: wl.WeakLensing, + number_counts_source: nc.NumberCounts, + sacc_data: sacc.Sacc, +) -> None: # The following disabling of pylint warnings are TEMPORARY. Disabling warnings is # generally not a good practice. In this case, the warnings are indicating that this # test is too complicated. @@ -730,7 +738,9 @@ def test_pt_systematics_zdep(weak_lensing_source, number_counts_source, sacc_dat assert np.allclose(cl_gg_theory, cells_gg_total, atol=0, rtol=1e-7) -def test_linear_bias_systematic(tools_with_vanilla_cosmology: ModelingTools): +def test_linear_bias_systematic( + tools_with_vanilla_cosmology: ModelingTools, +) -> None: a = nc_sys.LinearBiasSystematic("xxx") assert isinstance(a, nc_sys.LinearBiasSystematic) assert a.parameter_prefix == "xxx" diff --git a/tests/test_pyccl.py b/tests/test_pyccl.py index 2eb8cedd5..736558fdd 100644 --- a/tests/test_pyccl.py +++ b/tests/test_pyccl.py @@ -11,30 +11,32 @@ @pytest.fixture(name="cosmo_params_1", scope="session") -def fixture_cosmo_params_1(): +def fixture_cosmo_params_1() -> dict[str, float]: return {"Omega_c": 0.0, "Omega_b": 0.0, "h": 1.0, "A_s": 0.0, "n_s": 0.0} @pytest.fixture(name="cosmo_params_2", scope="session") -def fixture_cosmo_params_2(): +def fixture_cosmo_params_2() -> dict[str, float]: return {"Omega_c": 0.0, "Omega_b": 0.0, "h": 1.0, "A_s": 0.0, "n_s": 0.25} -def test_alias_of_cosmology_hashes_equal(cosmo_params_1): +def test_alias_of_cosmology_hashes_equal(cosmo_params_1: dict[str, float]) -> None: x = pyccl.Cosmology(**cosmo_params_1) y = x assert x == y assert hash(x) == hash(y) -def test_unequal_cosmologies_hash_unequal(cosmo_params_1, cosmo_params_2): +def test_unequal_cosmologies_hash_unequal( + cosmo_params_1: dict[str, float], cosmo_params_2: dict[str, float] +) -> None: x = pyccl.Cosmology(**cosmo_params_1) y = pyccl.Cosmology(**cosmo_params_2) assert x != y assert hash(x) != hash(y) -def test_equal_cosmologies_hash_equal(cosmo_params_1): +def test_equal_cosmologies_hash_equal(cosmo_params_1: dict[str, float]) -> None: # This test verifies expected (but not really desired) behavior. Two # Cosmology variables only have equal hashes when they are in fact they # are aliases for the same object. They also test as equal only if they diff --git a/tests/test_scale_cuts_with_window.py b/tests/test_scale_cuts_with_window.py index 62f29c53d..297df7c07 100644 --- a/tests/test_scale_cuts_with_window.py +++ b/tests/test_scale_cuts_with_window.py @@ -3,6 +3,7 @@ from pathlib import Path import numpy as np +import numpy.typing as npt import pyccl as ccl import pytest import sacc @@ -23,7 +24,9 @@ @pytest.fixture(name="window_function_data", scope="module") -def fixture_window_function(): +def fixture_window_function() -> ( + tuple[npt.NDArray[np.float64], npt.NDArray[np.int64], npt.NDArray[np.float64]] +): """Generate window function data.""" n_ell_bin = 5 ell_bin_edges = np.geomspace(10, 500, n_ell_bin + 1) @@ -43,7 +46,23 @@ def fixture_window_function(): @pytest.fixture(name="minimal_3x2pt_sacc", scope="module") -def fixture_minimal_3x2pt_sacc(window_function_data): +def fixture_minimal_3x2pt_sacc( + window_function_data: tuple[ + npt.NDArray[np.float64], npt.NDArray[np.int64], npt.NDArray[np.float64] + ], +) -> tuple[ + sacc.Sacc, + dict[ + str, + tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.int64], + npt.NDArray[np.float64], + ], + ], + ccl.Cosmology, +]: """Generate SACC data with window functions. At the moment, this only adds cosmic shear data.""" sacc_data = sacc.Sacc() @@ -104,7 +123,7 @@ def fixture_minimal_3x2pt_sacc(window_function_data): ("src1", "src1", 200), ], ) -def fixture_cut(request): +def fixture_cut(request: pytest.FixtureRequest) -> tuple[str, str, int]: return request.param @@ -118,8 +137,22 @@ def fixture_cut(request): def test_scale_cuts_with_bandpower_window_label( - minimal_3x2pt_sacc, cut, tmp_path: Path -): + minimal_3x2pt_sacc: tuple[ + sacc.Sacc, + dict[ + str, + tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.int64], + npt.NDArray[np.float64], + ], + ], + ccl.Cosmology, + ], + cut: tuple[str, str, int], + tmp_path: Path, +) -> None: """Test that the scale cuts with bandpower windows work correctly.""" sacc_data, ccl_cell, cosmo = minimal_3x2pt_sacc @@ -190,8 +223,23 @@ def test_scale_cuts_with_bandpower_window_label( ], ) def test_scale_cuts_with_bandpower_window_support( - minimal_3x2pt_sacc, cut, method, tmp_path: Path -): + minimal_3x2pt_sacc: tuple[ + sacc.Sacc, + dict[ + str, + tuple[ + npt.NDArray[np.float64], + npt.NDArray[np.float64], + npt.NDArray[np.int64], + npt.NDArray[np.float64], + ], + ], + ccl.Cosmology, + ], + cut: tuple[str, str, int], + method: tuple[TwoPointFilterMethod, float], + tmp_path: Path, +) -> None: """Test that the scale cuts with bandpower windows work correctly.""" sacc_data, ccl_cell, cosmo = minimal_3x2pt_sacc diff --git a/tests/test_two_point_deprecated.py b/tests/test_two_point_deprecated.py index 919c42874..8800a4a03 100644 --- a/tests/test_two_point_deprecated.py +++ b/tests/test_two_point_deprecated.py @@ -14,7 +14,7 @@ import pytest -def test_two_point_deprecation_warning(): +def test_two_point_deprecation_warning() -> None: """Test that importing two_point emits DeprecationWarning. This must be the first test that imports the module, as subsequent imports @@ -32,7 +32,7 @@ def test_two_point_deprecation_warning(): import firecrown.likelihood.two_point # noqa: F401 -def test_two_point_deprecation_warning_content(): +def test_two_point_deprecation_warning_content() -> None: """Test the deprecation warning mentions firecrown.likelihood. This test relies on module caching, so it won't see the warning. @@ -44,7 +44,7 @@ def test_two_point_deprecation_warning_content(): # If we get here without error, the import worked -def test_all_items_importable(): +def test_all_items_importable() -> None: """Test that all __all__ items can be imported from deprecated module.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood.two_point import ( @@ -67,7 +67,7 @@ def test_all_items_importable(): assert use_source_factory_metadata_index is not None -def test_items_identical_to_new_location(): +def test_items_identical_to_new_location() -> None: """Test that imported items are the same objects as in _two_point.""" # pylint: disable=import-outside-toplevel from firecrown.likelihood._two_point import ( @@ -111,7 +111,7 @@ def test_items_identical_to_new_location(): assert old_use_source_factory_metadata_index is use_source_factory_metadata_index -def test_module_import_as_alias(): +def test_module_import_as_alias() -> None: """Test importing the module with an alias.""" # pylint: disable=import-outside-toplevel import firecrown.likelihood.two_point as tp @@ -122,7 +122,7 @@ def test_module_import_as_alias(): assert hasattr(tp, "calculate_angular_cl") -def test_two_point_all_list(): +def test_two_point_all_list() -> None: """Test that __all__ is preserved in deprecated module.""" # pylint: disable=import-outside-toplevel import firecrown.likelihood.two_point diff --git a/tests/test_updatable_parameters.py b/tests/test_updatable_parameters.py index 075653ece..33f6c354b 100644 --- a/tests/test_updatable_parameters.py +++ b/tests/test_updatable_parameters.py @@ -24,14 +24,14 @@ ) -def test_register_new_updatable_parameter_with_no_arg(): +def test_register_new_updatable_parameter_with_no_arg() -> None: """Calling parameters.create() with no argument should return an SamplerParameter""" a_parameter = register_new_updatable_parameter(default_value=1.0) assert isinstance(a_parameter, SamplerParameter) -def test_register_new_updatable_parameter_with_float_arg(): +def test_register_new_updatable_parameter_with_float_arg() -> None: """Calling parameters.create() with a float argument should return a InternalParameter .""" a_parameter = register_new_updatable_parameter(value=1.5, default_value=1.0) @@ -39,7 +39,7 @@ def test_register_new_updatable_parameter_with_float_arg(): assert a_parameter.get_value() == 1.5 -def test_register_new_updatable_parameter_with_wrong_arg(): +def test_register_new_updatable_parameter_with_wrong_arg() -> None: """Calling parameters.create() with an org that is neither float nor None should raise a TypeError.""" with pytest.raises(TypeError): @@ -48,7 +48,7 @@ def test_register_new_updatable_parameter_with_wrong_arg(): ) -def test_register_new_updatable_parameter_with_shared_true(): +def test_register_new_updatable_parameter_with_shared_true() -> None: """Calling parameters.create() with shared=True should return a SamplerParameter with shared=True, meaning the parameter is shared across instances.""" a_parameter = register_new_updatable_parameter(default_value=1.0, shared=True) @@ -56,7 +56,7 @@ def test_register_new_updatable_parameter_with_shared_true(): assert a_parameter.shared is True -def test_register_new_updatable_parameter_with_shared_false(): +def test_register_new_updatable_parameter_with_shared_false() -> None: """Calling parameters.create() with shared=False (default) should return a SamplerParameter with shared=False, meaning each instance has its own parameter.""" a_parameter = register_new_updatable_parameter(default_value=1.0, shared=False) @@ -68,7 +68,7 @@ def test_register_new_updatable_parameter_with_shared_false(): assert a_parameter_default.shared is False -def test_sampler_parameter_shared_ignores_prefix(): +def test_sampler_parameter_shared_ignores_prefix() -> None: """When shared=True, the SamplerParameter should ignore the prefix in set_fullname, making it shared across all instances.""" sp = SamplerParameter(default_value=1.0, shared=True) @@ -80,7 +80,7 @@ def test_sampler_parameter_shared_ignores_prefix(): assert sp.fullname == "my_name" -def test_sampler_parameter_not_shared_uses_prefix(): +def test_sampler_parameter_not_shared_uses_prefix() -> None: """When shared=False (default), the SamplerParameter should use the prefix in set_fullname, making each instance have its own parameter.""" sp = SamplerParameter(default_value=1.0, shared=False) @@ -92,7 +92,7 @@ def test_sampler_parameter_not_shared_uses_prefix(): assert sp.fullname == "my_prefix_my_name" -def test_sampler_parameter_equality_with_shared(): +def test_sampler_parameter_equality_with_shared() -> None: """Test that SamplerParameter equality considers the shared attribute.""" sp1 = SamplerParameter(default_value=1.0, name="a", shared=True) sp2 = SamplerParameter(default_value=1.0, name="a", shared=True) @@ -102,7 +102,7 @@ def test_sampler_parameter_equality_with_shared(): assert sp1 != sp3 -def test_shared_parameter_across_updatable_instances(): +def test_shared_parameter_across_updatable_instances() -> None: """Test that shared=True parameters are consistent across multiple instances of the same Updatable class. @@ -161,7 +161,7 @@ def __init__(self, prefix: str | None = None): assert instance2.local_param == 20.0 -def test_get_default_params_with_shared_parameters(): +def test_get_default_params_with_shared_parameters() -> None: """Test that get_default_params and get_default_params_map work correctly when two instances of the same Updatable class share a parameter. @@ -218,7 +218,7 @@ def __init__(self, prefix: str | None = None): assert instance2.global_param == 1.0 -def test_required_parameters_length(): +def test_required_parameters_length() -> None: empty = RequiredParameters([]) assert len(empty) == 0 a = RequiredParameters([SamplerParameter(name="a", default_value=3.14)]) @@ -232,7 +232,7 @@ def test_required_parameters_length(): assert len(b) == 2 -def test_required_parameters_equality_testing(): +def test_required_parameters_equality_testing() -> None: a1 = RequiredParameters([SamplerParameter(name="a", default_value=2)]) a2 = RequiredParameters([SamplerParameter(name="a", default_value=2)]) assert a1 == a2 @@ -245,7 +245,7 @@ def test_required_parameters_equality_testing(): _ = a1 == 10 -def test_get_params_names_does_not_allow_mutation(): +def test_get_params_names_does_not_allow_mutation() -> None: """The caller of RequiredParameters.get_params_names should not be able to modify the state of the object on which the call was made.""" orig = RequiredParameters( @@ -261,7 +261,7 @@ def test_get_params_names_does_not_allow_mutation(): assert set(orig.get_params_names()) == {"a", "b"} -def test_params_map(): +def test_params_map() -> None: my_params = ParamsMap({"a": 1.0}) x = my_params.get_from_prefix_param(None, "a") assert x == 1 @@ -271,7 +271,7 @@ def test_params_map(): _ = my_params.get_from_prefix_param(None, "no_such_name") -def test_params_map_getitem(): +def test_params_map_getitem() -> None: a = ParamsMap({"a": 1.0}) assert a.get_unused_keys() == {"a"} assert a["a"] == 1 @@ -281,7 +281,7 @@ def test_params_map_getitem(): _ = a["b"] -def test_get_uses_params(): +def test_get_uses_params() -> None: a = ParamsMap({"a": 1.0}) assert a.used_keys == set() v = a.get("no_such_key", -1) @@ -292,7 +292,7 @@ def test_get_uses_params(): assert a.used_keys == {"a"} -def test_params_map_wrong_type(): +def test_params_map_wrong_type() -> None: with pytest.raises( TypeError, match="Value for parameter a is not a float or a list of floats.*" ): @@ -301,7 +301,7 @@ def test_params_map_wrong_type(): ) -def test_params_map_wrong_type_list(): +def test_params_map_wrong_type_list() -> None: with pytest.raises( TypeError, match="Value for parameter a is not a float or a list of floats.*" ): @@ -310,26 +310,26 @@ def test_params_map_wrong_type_list(): ) -def test_parameter_get_full_name_reject_empty_name(): +def test_parameter_get_full_name_reject_empty_name() -> None: with pytest.raises(ValueError): _ = parameter_get_full_name(None, "") with pytest.raises(ValueError): _ = parameter_get_full_name("cow", "") -def test_parameter_get_full_name_with_prefix(): +def test_parameter_get_full_name_with_prefix() -> None: full_name = parameter_get_full_name("my_prefix", "my_name") # TODO: do we really want to allow underscores in parameter names, when we # are using the underscore as our separator? assert full_name == "my_prefix_my_name" -def test_parameter_get_full_name_without_prefix(): +def test_parameter_get_full_name_without_prefix() -> None: full_name = parameter_get_full_name(None, "nomen_foo") assert full_name == "nomen_foo" -def test_derived_parameter_scalar(): +def test_derived_parameter_scalar() -> None: derived_param = DerivedParameter("sec1", "name1", 3.14) assert isinstance(derived_param.get_val(), float) @@ -337,7 +337,7 @@ def test_derived_parameter_scalar(): assert derived_param.get_full_name() == "sec1--name1" -def test_derived_parameter_wrong_type(): +def test_derived_parameter_wrong_type() -> None: """Try instantiating DerivedParameter objects with wrong types.""" with pytest.raises(TypeError): @@ -354,7 +354,7 @@ def test_derived_parameter_wrong_type(): ) -def test_derived_parameters_collection(): +def test_derived_parameters_collection() -> None: olist = [ DerivedParameter("sec1", "name1", 3.14), DerivedParameter("sec2", "name2", 2.72), @@ -365,7 +365,7 @@ def test_derived_parameters_collection(): assert orig.get_derived_list() == olist -def test_derived_parameters_collection_rejects_bad_list(): +def test_derived_parameters_collection_rejects_bad_list() -> None: badlist = [1, 3, 5] with pytest.raises(TypeError): # We have to tell mypy to ignore the type error on the @@ -374,7 +374,7 @@ def test_derived_parameters_collection_rejects_bad_list(): _ = DerivedParameterCollection(badlist) # type: ignore -def test_derived_parameters_collection_add(): +def test_derived_parameters_collection_add() -> None: olist = [ DerivedParameter("sec1", "name1", 3.14), DerivedParameter("sec2", "name2", 2.72), @@ -391,7 +391,7 @@ def test_derived_parameters_collection_add(): assert val == derived_parameter.get_val() -def test_derived_parameters_collection_add_iter(): +def test_derived_parameters_collection_add_iter() -> None: olist1 = [ DerivedParameter("sec1", "name1", 3.14), DerivedParameter("sec2", "name2", 2.72), @@ -415,14 +415,14 @@ def test_derived_parameters_collection_add_iter(): assert val == derived_parameter.get_val() -def test_derived_parameter_eq(): +def test_derived_parameter_eq() -> None: dv1 = DerivedParameter("sec1", "name1", 3.14) dv2 = DerivedParameter("sec1", "name1", 3.14) assert dv1 == dv2 -def test_derived_parameter_eq_invalid(): +def test_derived_parameter_eq_invalid() -> None: dv1 = DerivedParameter("sec1", "name1", 3.14) with pytest.raises( @@ -433,7 +433,7 @@ def test_derived_parameter_eq_invalid(): _ = dv1 == 1.0 -def test_derived_parameters_collection_eq(): +def test_derived_parameters_collection_eq() -> None: olist1 = [ DerivedParameter("sec1", "name1", 3.14), DerivedParameter("sec2", "name2", 2.72), @@ -451,7 +451,7 @@ def test_derived_parameters_collection_eq(): assert dpc1 == dpc2 -def test_derived_parameters_collection_eq_invalid(): +def test_derived_parameters_collection_eq_invalid() -> None: olist1 = [ DerivedParameter("sec1", "name1", 3.14), DerivedParameter("sec2", "name2", 2.72), @@ -467,47 +467,47 @@ def test_derived_parameters_collection_eq_invalid(): _ = dpc1 == 1.0 -def test_sampler_parameter_no_prefix(): +def test_sampler_parameter_no_prefix() -> None: sp = SamplerParameter(name="name1", default_value=1) assert sp.name == "name1" assert sp.fullname == "name1" -def test_sampler_parameter_with_prefix(): +def test_sampler_parameter_with_prefix() -> None: sp = SamplerParameter(name="name1", prefix="prefix1", default_value=1) assert sp.name == "name1" assert sp.fullname == "prefix1_name1" -def test_sampler_parameter_with_value(): +def test_sampler_parameter_with_value() -> None: sp = SamplerParameter(name="name1", default_value=2) assert sp is not None -def test_sampler_parameter_with_value_and_prefix(): +def test_sampler_parameter_with_value_and_prefix() -> None: sp = SamplerParameter(name="name1", prefix="prefix1", default_value=2) assert sp is not None -def test_sampler_parameter_with_value_and_prefix_and_fullname(): +def test_sampler_parameter_with_value_and_prefix_and_fullname() -> None: sp = SamplerParameter(name="name1", prefix="prefix1", default_value=2) assert sp.fullname == "prefix1_name1" -def test_sample_parameter_hash(): +def test_sample_parameter_hash() -> None: sp1 = SamplerParameter(name="name1", default_value=3.14) sp2 = SamplerParameter(name="name1", default_value=3.14) assert hash(sp1) == hash(sp2) -def test_sampler_parameter_eq(): +def test_sampler_parameter_eq() -> None: sp1 = SamplerParameter(name="name1", default_value=3.14) sp2 = SamplerParameter(name="name1", default_value=3.14) assert sp1 == sp2 -def test_sampler_parameter_eq_invalid(): +def test_sampler_parameter_eq_invalid() -> None: sp1 = SamplerParameter(name="name1", default_value=3.14) with pytest.raises( NotImplementedError, @@ -519,25 +519,25 @@ def test_sampler_parameter_eq_invalid(): _ = sp1 == 1.0 -def test_sampler_parameter_ne(): +def test_sampler_parameter_ne() -> None: sp1 = SamplerParameter(name="name1", default_value=3.14) sp2 = SamplerParameter(name="name2", default_value=3.14) assert sp1 != sp2 -def test_sampler_parameter_default_value(): +def test_sampler_parameter_default_value() -> None: sp = SamplerParameter(name="name1", default_value=3.14) assert sp.get_default_value() == 3.14 -def test_sampler_parameter_default_value_no_name(): +def test_sampler_parameter_default_value_no_name() -> None: sp = SamplerParameter(default_value=2.72) with pytest.raises(ValueError, match="Parameter name is not set"): _ = sp.name -def test_required_parameters_get_names(): +def test_required_parameters_get_names() -> None: rp = RequiredParameters( [ SamplerParameter(name="name1", default_value=3.14), @@ -547,7 +547,7 @@ def test_required_parameters_get_names(): assert set(rp.get_params_names()) == {"name1", "name2"} -def test_required_parameters_get_default_values(): +def test_required_parameters_get_default_values() -> None: rp = RequiredParameters( [ SamplerParameter(name="name1", default_value=3.14), @@ -558,7 +558,7 @@ def test_required_parameters_get_default_values(): assert rp.get_default_values() == {"name1": 3.14, "name2": 2.72, "name3": 2.3} -def test_add_required_parameter(): +def test_add_required_parameter() -> None: coll = DerivedParameterCollection([]) coll.add_required_parameter( DerivedParameter(section="barnyard", name="cow", val=1.0) @@ -574,14 +574,14 @@ def test_add_required_parameter(): ) -def test_setting_internal_parameter(): +def test_setting_internal_parameter() -> None: a_parameter = register_new_updatable_parameter(value=1.0, default_value=2.0) assert a_parameter.value == 1.0 a_parameter.set_value(2.0) assert a_parameter.value == 2.0 -def test_used_and_unused_params(): +def test_used_and_unused_params() -> None: """Test that get_unused_keys works as expected.""" params = ParamsMap({"a": 1.0, "b": 2.0, "c": 3.0}) @@ -608,7 +608,7 @@ def test_used_and_unused_params(): assert unused == set() -def test_handle_unused_params(): +def test_handle_unused_params() -> None: """Test that handle_unused_params works as expected.""" params = ParamsMap({"a": 1.0, "b": 2.0, "c": 3.0}) @@ -645,7 +645,7 @@ def test_handle_unused_params(): handle_unused_params(params=params, updated_records=[], raise_on_unused=True) -def test_handle_unused_params_includes_updated_records_in_message(): +def test_handle_unused_params_includes_updated_records_in_message() -> None: params = ParamsMap({"x": 1.0}) # Create a simple UpdatableUsageRecord structure @@ -690,7 +690,7 @@ def test_handle_unused_params_includes_updated_records_in_message(): assert "Parent(p) => Child(c):" in err_msg or "Parent(p):" in err_msg -def test_params_map_union(): +def test_params_map_union() -> None: p1 = ParamsMap({"a": 1.0}) p2 = ParamsMap({"b": 2.0}) p3 = p1.union(p2) @@ -717,7 +717,7 @@ def test_params_map_union(): p1.union(p2) -def test_params_get(): +def test_params_get() -> None: params = ParamsMap({"a": 1.0}) assert params.get("a") == 1.0 assert params.get("b", 2.0) == 2.0 @@ -725,13 +725,13 @@ def test_params_get(): params.get("b") -def test_params_map_items(): +def test_params_map_items() -> None: d = {"a": 1.0, "b": 2.0} params = ParamsMap(d) assert params.items() == d.items() -def test_params_map_lower_case_lookup(): +def test_params_map_lower_case_lookup() -> None: """Ensure get_from_full_name respects the lower-case fallback when enabled.""" p = ParamsMap({"my_key": 4.2}) # lookup with different case should fail until we enable lower-case handling @@ -744,14 +744,14 @@ def test_params_map_lower_case_lookup(): assert val == 4.2 -def test_params_map_list_with_ints_raises_typeerror(): +def test_params_map_list_with_ints_raises_typeerror() -> None: """A list value containing ints (not floats) should trigger the list element type check and raise TypeError.""" with pytest.raises(TypeError, match="Value for parameter a is not a float"): _ = ParamsMap({"a": [1, 2.0]}) # type: ignore[arg-type] -def test_params_map_setitem(): +def test_params_map_setitem() -> None: """Test ParamsMap.__setitem__() method for setting values.""" params = ParamsMap({"a": 1.0}) params["b"] = 2.0 @@ -760,7 +760,7 @@ def test_params_map_setitem(): assert params["a"] == 3.0 -def test_params_map_contains(): +def test_params_map_contains() -> None: """Test ParamsMap.__contains__() method with in operator.""" params = ParamsMap({"a": 1.0, "b": 2.0}) assert "a" in params @@ -769,7 +769,7 @@ def test_params_map_contains(): assert "nonexistent" not in params -def test_params_map_copy(): +def test_params_map_copy() -> None: """Test ParamsMap.copy() creates an independent copy.""" original = ParamsMap({"a": 1.0, "b": 2.0}) _ = original["a"] # Mark 'a' as used @@ -795,7 +795,7 @@ def test_params_map_copy(): assert original.lower_case is False -def test_params_map_update_duplicate_key(): +def test_params_map_update_duplicate_key() -> None: """Test ParamsMap.update() raises error for duplicate keys.""" params = ParamsMap({"a": 1.0}) @@ -808,7 +808,7 @@ def test_params_map_update_duplicate_key(): params.update({"a": 3.0}) -def test_params_map_keys(): +def test_params_map_keys() -> None: """Test ParamsMap.keys() returns correct set of keys.""" params = ParamsMap({"a": 1.0, "b": 2.0, "c": 3.0}) keys = params.keys() @@ -821,7 +821,7 @@ def test_params_map_keys(): assert params.keys() == {"a", "b", "c"} -def test_required_parameters_subtraction(): +def test_required_parameters_subtraction() -> None: """Test RequiredParameters.__sub__() subtraction operator.""" param_a = SamplerParameter(name="a", default_value=1.0) param_b = SamplerParameter(name="b", default_value=2.0) @@ -843,7 +843,7 @@ def test_required_parameters_subtraction(): assert set(params2.get_params_names()) == {"b", "d"} -def test_required_parameters_addition(): +def test_required_parameters_addition() -> None: """Test RequiredParameters.__add__() addition operator.""" param_a = SamplerParameter(name="a", default_value=1.0) param_b = SamplerParameter(name="b", default_value=2.0) @@ -864,7 +864,7 @@ def test_required_parameters_addition(): assert set(params2.get_params_names()) == {"c"} -def test_sampler_parameter_set_fullname(): +def test_sampler_parameter_set_fullname() -> None: """Test SamplerParameter.set_fullname() method.""" sp = SamplerParameter(default_value=1.0) @@ -885,7 +885,7 @@ def test_sampler_parameter_set_fullname(): assert sp.fullname == "another_name" -def test_params_map_union_no_common_keys(): +def test_params_map_union_no_common_keys() -> None: """Test ParamsMap.union() when there are no common keys. This covers the branch where the for loop over common keys is not entered. @@ -902,7 +902,7 @@ def test_params_map_union_no_common_keys(): assert p3.get_from_full_name("d") == 4.0 -def test_params_map_union_common_keys_same_values(): +def test_params_map_union_common_keys_same_values() -> None: """Test ParamsMap.union() when common keys have the same values. This covers the branch where common keys exist but don't trigger @@ -919,7 +919,7 @@ def test_params_map_union_common_keys_same_values(): assert p3.get_from_full_name("c") == 3.0 -def test_params_map_lower_case_key_not_found(): +def test_params_map_lower_case_key_not_found() -> None: """Test ParamsMap with lower_case enabled but key still not found. This covers the branch where lower_case is True but the lowercased @@ -933,7 +933,7 @@ def test_params_map_lower_case_key_not_found(): _ = p.get_from_full_name("NONEXISTENT") -def test_handle_unused_params_all_used(): +def test_handle_unused_params_all_used() -> None: """Test handle_unused_params when all parameters are used. This covers the early return branch when there are no unused keys. @@ -949,7 +949,7 @@ def test_handle_unused_params_all_used(): handle_unused_params(params=params, updated_records=[], raise_on_unused=True) -def test_params_map_with_list_of_floats(): +def test_params_map_with_list_of_floats() -> None: """Test ParamsMap accepts a list of floats as a valid value. This covers the branch in validate_params_map_value where diff --git a/tests/test_utils.py b/tests/test_utils.py index de4a90777..7f3d1091b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,6 +7,7 @@ from typing import cast import numpy as np +import numpy.typing as npt import pyccl import pytest import sacc @@ -28,19 +29,23 @@ save_to_sacc, upper_triangle_indices, ) +from firecrown.likelihood._statistic import TrivialStatistic +from firecrown.modeling_tools import ModelingTools -def test_upper_triangle_indices_nonzero(): +def test_upper_triangle_indices_nonzero() -> None: indices = list(upper_triangle_indices(3)) assert indices == [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)] -def test_upper_triangle_indices_zero(): +def test_upper_triangle_indices_zero() -> None: indices = list(upper_triangle_indices(0)) assert not indices -def test_save_to_sacc(trivial_stats, sacc_data_for_trivial_stat): +def test_save_to_sacc( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: stat = trivial_stats[0] stat.read(sacc_data_for_trivial_stat) idx = np.arange(stat.count) @@ -55,7 +60,9 @@ def test_save_to_sacc(trivial_stats, sacc_data_for_trivial_stat): assert all(new_sacc.data[i].value == d for i, d in zip(idx, new_data_vector)) -def test_save_to_sacc_strict_fail(trivial_stats, sacc_data_for_trivial_stat): +def test_save_to_sacc_strict_fail( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: stat = trivial_stats[0] stat.read(sacc_data_for_trivial_stat) idx = np.arange(stat.count - 1) @@ -70,7 +77,9 @@ def test_save_to_sacc_strict_fail(trivial_stats, sacc_data_for_trivial_stat): ) -def test_save_to_sacc_non_sttrict(trivial_stats, sacc_data_for_trivial_stat): +def test_save_to_sacc_non_sttrict( + trivial_stats: list[TrivialStatistic], sacc_data_for_trivial_stat: sacc.Sacc +) -> None: stat = trivial_stats[0] stat.read(sacc_data_for_trivial_stat) idx = np.arange(stat.count) @@ -98,7 +107,7 @@ def _make_minimal_sacc() -> sacc.Sacc: @pytest.mark.parametrize("suffix", [".fits", *_AMBIGUOUS_SUFFIXES]) -def test_load_sacc_data_loads_fits(tmp_path, suffix): +def test_load_sacc_data_loads_fits(tmp_path: Path, suffix: str) -> None: """load_sacc_data reads FITS content, including from ambiguous extensions. When the extension does not announce a format (e.g. the ``.sacc`` files @@ -115,7 +124,7 @@ def test_load_sacc_data_loads_fits(tmp_path, suffix): @pytest.mark.parametrize("suffix", [".hdf5", ".h5", *_AMBIGUOUS_SUFFIXES]) -def test_load_sacc_data_loads_hdf5(tmp_path, suffix): +def test_load_sacc_data_loads_hdf5(tmp_path: Path, suffix: str) -> None: """load_sacc_data reads HDF5 content, including from ambiguous extensions. This is the case that regressed under SACC 2.2: an HDF5 file named with an @@ -131,7 +140,7 @@ def test_load_sacc_data_loads_hdf5(tmp_path, suffix): assert len(loaded.tracers) == 1 -def test_load_sacc_data_accepts_string_path(tmp_path): +def test_load_sacc_data_accepts_string_path(tmp_path: Path) -> None: """load_sacc_data accepts a string path as well as a Path object.""" input_file = tmp_path / "data.sacc" _make_minimal_sacc().save_fits(str(input_file), overwrite=True) @@ -141,13 +150,13 @@ def test_load_sacc_data_accepts_string_path(tmp_path): assert isinstance(loaded, sacc.Sacc) -def test_load_sacc_data_missing_file_raises(tmp_path): +def test_load_sacc_data_missing_file_raises(tmp_path: Path) -> None: """load_sacc_data raises FileNotFoundError for a non-existent file.""" with pytest.raises(FileNotFoundError, match="SACC file not found"): load_sacc_data(tmp_path / "does_not_exist.sacc") -def test_load_sacc_data_unrecognized_content_raises(tmp_path): +def test_load_sacc_data_unrecognized_content_raises(tmp_path: Path) -> None: """load_sacc_data raises ValueError when content is neither FITS nor HDF5.""" bad_file = tmp_path / "corrupted.sacc" bad_file.write_text("This is not a valid SACC file format") @@ -164,16 +173,16 @@ def test_load_sacc_data_unrecognized_content_raises(tmp_path): @pytest.mark.parametrize("value", ["some/path.sacc", Path("some/path.sacc")]) -def test_ensure_path_returns_path(value): +def test_ensure_path_returns_path(value: str | Path) -> None: """ensure_path returns a Path for both str and Path inputs.""" result = ensure_path(value) assert isinstance(result, Path) assert result == Path("some/path.sacc") -def test_compare_optional_arrays_(): - x = np.array([1, 2, 3]) - y = np.array([1, 2, 3]) +def test_compare_optional_arrays_() -> None: + x: npt.NDArray[np.int64] = np.array([1, 2, 3]) + y: npt.NDArray[np.int64] = np.array([1, 2, 3]) assert compare_optional_arrays(x, y) z = np.array([1, 2, 4]) @@ -194,7 +203,7 @@ def test_base_model_from_yaml_wrong() -> None: _ = base_model_from_yaml(cast(type[BaseModel], str), "wrong") -def test_compare_optionals(): +def test_compare_optionals() -> None: x = "test" y = "test" assert compare_optionals(x, y) @@ -216,7 +225,7 @@ def test_compare_optionals(): name="limber_method", params=[ClLimberMethod.GSL_SPLINE, ClLimberMethod.GSL_QAG_QUAD], ) -def fixture_limber_method(request): +def fixture_limber_method(request: pytest.FixtureRequest) -> ClLimberMethod: return request.param @@ -224,17 +233,17 @@ def fixture_limber_method(request): name="limber_max_error", params=[None, 0.1, 0.2], ) -def fixture_limber_max_error(request): +def fixture_limber_max_error(request: pytest.FixtureRequest) -> float | None: return request.param @pytest.fixture(name="fkem_chi_min", params=[None, 0.05, 0.082]) -def fixture_fkem_chi_min(request): +def fixture_fkem_chi_min(request: pytest.FixtureRequest) -> float | None: return request.param @pytest.fixture(name="fkem_Nchi", params=[None, 10, 20]) -def fixture_fkem_Nchi(request): +def fixture_fkem_Nchi(request: pytest.FixtureRequest) -> int | None: return request.param @@ -242,11 +251,11 @@ def fixture_fkem_Nchi(request): name="l_limber", params=[40, 60], ) -def fixture_l_limber(request): +def fixture_l_limber(request: pytest.FixtureRequest) -> int: return request.param -def test_cl_integration_options_limber(limber_method: ClLimberMethod): +def test_cl_integration_options_limber(limber_method: ClLimberMethod) -> None: int_options = ClIntegrationOptions( method=ClIntegrationMethod.LIMBER, limber_method=limber_method ) @@ -265,7 +274,7 @@ def test_cl_integration_options_limber(limber_method: ClLimberMethod): assert int_options == int_options2 -def test_cl_integration_options_limber_yaml(limber_method: ClLimberMethod): +def test_cl_integration_options_limber_yaml(limber_method: ClLimberMethod) -> None: int_options_yaml = f""" method: LIMBER limber_method: {limber_method.lower()} @@ -280,7 +289,7 @@ def test_cl_integration_options_fkem_auto( limber_max_error: float | None, fkem_chi_min: float | None, fkem_Nchi: int | None, -): +) -> None: int_options = ClIntegrationOptions( method=ClIntegrationMethod.FKEM_AUTO, limber_method=limber_method, @@ -322,7 +331,7 @@ def test_cl_integration_options_fkem_auto_yaml( limber_max_error: float | None, fkem_chi_min: float | None, fkem_Nchi: int | None, -): +) -> None: int_options_yaml = f""" method: FKEM_AUTO limber_method: {limber_method.lower()} @@ -343,7 +352,7 @@ def test_cl_integration_options_fkem_l_limber( fkem_chi_min: float | None, fkem_Nchi: int | None, l_limber: int, -): +) -> None: int_options = ClIntegrationOptions( method=ClIntegrationMethod.FKEM_L_LIMBER, limber_method=limber_method, @@ -381,7 +390,7 @@ def test_cl_integration_options_fkem_l_limber_yaml( fkem_chi_min: float | None, fkem_Nchi: int | None, l_limber: int, -): +) -> None: int_options_yaml = f""" method: FKEM_L_LIMBER limber_method: {limber_method.lower()} @@ -397,7 +406,7 @@ def test_cl_integration_options_fkem_l_limber_yaml( assert int_options.fkem_Nchi == fkem_Nchi -def test_cl_integration_options_yaml_invalid(): +def test_cl_integration_options_yaml_invalid() -> None: int_options_yaml = """ method: Im_not_a_valid_method limber_method: qag_quad @@ -451,7 +460,7 @@ def test_cl_integration_options_yaml_invalid(): base_model_from_yaml(ClIntegrationOptions, int_options_yaml) -def test_cl_integration_options_limber_invalid(): +def test_cl_integration_options_limber_invalid() -> None: with pytest.raises( ValueError, match="l_limber is incompatible with ClIntegrationMethod.LIMBER" ): @@ -490,7 +499,7 @@ def test_cl_integration_options_limber_invalid(): ) -def test_cl_integration_options_fkem_auto_invalid(): +def test_cl_integration_options_fkem_auto_invalid() -> None: with pytest.raises( ValueError, match="l_limber is incompatible with ClIntegrationMethod.FKEM_AUTO", @@ -502,7 +511,7 @@ def test_cl_integration_options_fkem_auto_invalid(): ) -def test_cl_integration_options_fkem_l_limber_invalid(): +def test_cl_integration_options_fkem_l_limber_invalid() -> None: with pytest.raises( ValueError, match="limber_max_error is incompatible with ClIntegrationMethod.FKEM_L_LIMBER", @@ -524,7 +533,7 @@ def test_cl_integration_options_fkem_l_limber_invalid(): ) -def test_cl_integration_test_cmparison(): +def test_cl_integration_test_cmparison() -> None: int_options = ClIntegrationOptions( method=ClIntegrationMethod.LIMBER, limber_method=ClLimberMethod.GSL_SPLINE ) @@ -536,7 +545,7 @@ def test_cl_integration_test_cmparison(): ) -def test_make_log_interpolator_positive(): +def test_make_log_interpolator_positive() -> None: x = np.arange(1, 3000) y = np.exp(x / 1.0e3) * x**1.43 @@ -545,7 +554,7 @@ def test_make_log_interpolator_positive(): assert_allclose(f(x), y, atol=0.0, rtol=1e-12) -def test_make_log_interpolator_negative(): +def test_make_log_interpolator_negative() -> None: x = np.arange(1, 3000) y = np.exp(x / 1.0e3) * (x - 1000.5) @@ -554,7 +563,7 @@ def test_make_log_interpolator_negative(): assert_allclose(f(x), y, atol=0.0, rtol=1e-12) -def test_cached_angular_cl(tools_with_vanilla_cosmology): +def test_cached_angular_cl(tools_with_vanilla_cosmology: ModelingTools) -> None: tools = tools_with_vanilla_cosmology cosmo = tools.get_ccl_cosmology() @@ -572,7 +581,9 @@ def test_cached_angular_cl(tools_with_vanilla_cosmology): assert np.all(np.isfinite(cl)) -def test_cached_angular_cl_with_options(tools_with_vanilla_cosmology): +def test_cached_angular_cl_with_options( + tools_with_vanilla_cosmology: ModelingTools, +) -> None: tools = tools_with_vanilla_cosmology cosmo = tools.get_ccl_cosmology() @@ -605,11 +616,15 @@ def test_cached_angular_cl_with_options(tools_with_vanilla_cosmology): (ClIntegrationMethod.FKEM_L_LIMBER, "fkem_l_limber"), ], ) -def test_yaml_serializable_to_yaml(method, expected): +def test_yaml_serializable_to_yaml(method: ClIntegrationMethod, expected: str) -> None: # Add test to verify all enum values are covered all_methods = set(ClIntegrationMethod) + # Pytest's store_mark adds pytestmark; callable annotations cannot express it. test_methods = { - param[0] for param in test_yaml_serializable_to_yaml.pytestmark[0].args[1] + param[0] + for param in ( + test_yaml_serializable_to_yaml.pytestmark # type: ignore[attr-defined] + )[0].args[1] } assert ( all_methods == test_methods @@ -627,11 +642,17 @@ def test_yaml_serializable_to_yaml(method, expected): ("fkem_l_limber", ClIntegrationMethod.FKEM_L_LIMBER), ], ) -def test_yaml_serializable_from_yaml(yaml_str, expected): +def test_yaml_serializable_from_yaml( + yaml_str: str, expected: ClIntegrationMethod +) -> None: # Add test to verify all enum values are covered all_methods = set(ClIntegrationMethod) + # Pytest's store_mark adds pytestmark; callable annotations cannot express it. test_methods = { - param[1] for param in test_yaml_serializable_from_yaml.pytestmark[0].args[1] + param[1] + for param in ( + test_yaml_serializable_from_yaml.pytestmark # type: ignore[attr-defined] + )[0].args[1] } assert ( all_methods == test_methods diff --git a/tests/test_version.py b/tests/test_version.py index d3d140f2a..72360d1ca 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -11,7 +11,7 @@ SEMVER_PATTERN = re.compile(r"^\d+\.\d+\.\d+(?:a\d+)?$") -def test_version_format(): +def test_version_format() -> None: """Test that version follows semantic versioning format. This test validates the version format rather than a specific value, diff --git a/tests/updatable/test_base.py b/tests/updatable/test_base.py index ee5fd08da..bcf9014cb 100644 --- a/tests/updatable/test_base.py +++ b/tests/updatable/test_base.py @@ -3,6 +3,7 @@ from itertools import permutations import numpy as np +import numpy.typing as npt import pytest from firecrown import updatable @@ -24,13 +25,13 @@ # pylint: disable-msg=E1101 -def test_get_params_names(): +def test_get_params_names() -> None: obj = SimpleUpdatable() found_names = obj.get_params_names() assert set(found_names) == {"x", "y"} -def test_simple_updatable(): +def test_simple_updatable() -> None: obj = SimpleUpdatable() expected_params = RequiredParameters( [ @@ -52,7 +53,7 @@ def test_simple_updatable(): assert obj.is_updated() -def test_set_sampler_parameter(): +def test_set_sampler_parameter() -> None: my_updatable = MinimalUpdatable() my_param = updatable.register_new_updatable_parameter(default_value=42.0) my_param.set_fullname(prefix=None, name="the_meaning_of_life") @@ -62,7 +63,7 @@ def test_set_sampler_parameter(): assert my_updatable.the_meaning_of_life is None -def test_set_sampler_parameter_rejects_internal_parameter(): +def test_set_sampler_parameter_rejects_internal_parameter() -> None: my_updatable = MinimalUpdatable() my_param = updatable.register_new_updatable_parameter( value=42.0, default_value=41.0 @@ -72,7 +73,7 @@ def test_set_sampler_parameter_rejects_internal_parameter(): my_updatable.set_sampler_parameter(my_param) # type: ignore[arg-type] -def test_set_sampler_parameter_rejects_duplicates(): +def test_set_sampler_parameter_rejects_duplicates() -> None: my_updatable = MinimalUpdatable() my_param = updatable.register_new_updatable_parameter(default_value=42.0) my_param.set_fullname(prefix=None, name="the_meaning_of_life") @@ -85,7 +86,7 @@ def test_set_sampler_parameter_rejects_duplicates(): my_updatable.set_sampler_parameter(my_param_same) -def test_set_internal_parameter(): +def test_set_internal_parameter() -> None: my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", @@ -96,7 +97,7 @@ def test_set_internal_parameter(): assert my_updatable.the_meaning_of_life == 1.0 -def test_set_parameter_using_internal_parameter(): +def test_set_parameter_using_internal_parameter() -> None: my_updatable = MinimalUpdatable() ip = updatable.InternalParameter(2112) my_updatable.set_parameter("epic_Rush_album", ip) @@ -105,7 +106,7 @@ def test_set_parameter_using_internal_parameter(): assert my_updatable.epic_Rush_album == 2112 -def test_set_internal_parameter_rejects_sampler_parameter(): +def test_set_internal_parameter_rejects_sampler_parameter() -> None: my_updatable = MinimalUpdatable() with pytest.raises(TypeError): my_updatable.set_internal_parameter( @@ -116,7 +117,7 @@ def test_set_internal_parameter_rejects_sampler_parameter(): ) -def test_set_internal_parameter_rejects_duplicates(): +def test_set_internal_parameter_rejects_duplicates() -> None: my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", @@ -130,7 +131,7 @@ def test_set_internal_parameter_rejects_duplicates(): ) -def test_set_parameter(): +def test_set_parameter() -> None: my_updatable = MinimalUpdatable() my_updatable.set_parameter( "the_meaning_of_life", @@ -148,7 +149,7 @@ def test_set_parameter(): assert my_updatable.no_meaning_of_life is None -def test_update_rejects_internal_parameters(): +def test_update_rejects_internal_parameters() -> None: my_updatable = MinimalUpdatable() my_updatable.set_internal_parameter( "the_meaning_of_life", @@ -167,7 +168,7 @@ def test_update_rejects_internal_parameters(): assert my_updatable.the_meaning_of_life == 2.0 -def test_setattr_with_list_of_updatables(): +def test_setattr_with_list_of_updatables() -> None: """Test setting an attribute to a list of Updatable objects. This tests the code path in __setattr__ that handles iterables containing @@ -195,7 +196,7 @@ def test_setattr_with_list_of_updatables(): assert child2.a == 4.0 -def test_update_already_updated_with_updated_record(): +def test_update_already_updated_with_updated_record() -> None: """Test calling update() twice with updated_record parameter. When an object is already updated and update() is called again with @@ -222,7 +223,7 @@ def test_update_already_updated_with_updated_record(): assert updated_records2[0].internal_params == [] -def test_update_already_updated_without_record(): +def test_update_already_updated_without_record() -> None: """Test calling update() twice without updated_record parameter. When an object is already updated and update() is called again without @@ -249,7 +250,7 @@ def test_update_already_updated_without_record(): assert obj.y == 2.0 -def test_reset_when_not_updated(): +def test_reset_when_not_updated() -> None: """Test calling reset() on an object that hasn't been updated. Should return early without doing anything (lines 234-235 in _base.py). @@ -270,7 +271,7 @@ def test_reset_when_not_updated(): assert obj.y is None -def test_reset_with_nested_updatables_and_sampler_params(): +def test_reset_with_nested_updatables_and_sampler_params() -> None: """Test reset() with nested updatables and sampler parameters. Verifies that reset(): @@ -306,7 +307,7 @@ def test_reset_with_nested_updatables_and_sampler_params(): assert child.a is None -def test_tuple_attribute_adds_to_updatables(): +def test_tuple_attribute_adds_to_updatables() -> None: """Test that setting a tuple attribute adds Updatable elements to _updatables. When an Updatable container has an attribute set to an iterable containing @@ -327,7 +328,9 @@ def test_tuple_attribute_adds_to_updatables(): @pytest.fixture(name="nested_updatables", params=permutations(range(3))) -def fixture_nested_updatables(request): +def fixture_nested_updatables( + request: pytest.FixtureRequest, +) -> npt.NDArray[np.object_]: updatables = np.array( [MinimalUpdatable(), SimpleUpdatable(), UpdatableWithDerived()] ) @@ -340,7 +343,9 @@ def fixture_nested_updatables(request): return updatables -def test_nesting_updatables_missing_parameters(nested_updatables): +def test_nesting_updatables_missing_parameters( + nested_updatables: npt.NDArray[np.object_], +) -> None: base = nested_updatables[0] assert isinstance(base, Updatable) @@ -373,7 +378,9 @@ def test_nesting_updatables_missing_parameters(nested_updatables): assert my_updatable.is_updated() -def test_nesting_updatables_required_parameters(nested_updatables): +def test_nesting_updatables_required_parameters( + nested_updatables: npt.NDArray[np.object_], +) -> None: base = nested_updatables[0] assert isinstance(base, Updatable) @@ -388,7 +395,9 @@ def test_nesting_updatables_required_parameters(nested_updatables): ) -def test_nesting_updatables_derived_parameters(nested_updatables): +def test_nesting_updatables_derived_parameters( + nested_updatables: npt.NDArray[np.object_], +) -> None: base = nested_updatables[0] assert isinstance(base, Updatable) diff --git a/tests/updatable/test_collection.py b/tests/updatable/test_collection.py index ec32fec1e..7706344a7 100644 --- a/tests/updatable/test_collection.py +++ b/tests/updatable/test_collection.py @@ -15,7 +15,7 @@ # pylint: disable-msg=E1101 -def test_updatable_collection_record(): +def test_updatable_collection_record() -> None: """Test record creation for UpdatableCollection.""" coll = UpdatableCollection([SimpleUpdatable("first"), MinimalUpdatable("second")]) @@ -36,7 +36,7 @@ def test_updatable_collection_record(): assert second_record.sampler_params == ["a"] -def test_updatable_collection_appends(): +def test_updatable_collection_appends() -> None: coll: UpdatableCollection[Updatable] = UpdatableCollection() assert len(coll) == 0 @@ -67,7 +67,7 @@ def test_updatable_collection_appends(): ) -def test_updatable_collection_updates(): +def test_updatable_collection_updates() -> None: coll: UpdatableCollection[Updatable] = UpdatableCollection() assert len(coll) == 0 @@ -85,7 +85,7 @@ def test_updatable_collection_updates(): assert first.y == 5.5 -def test_updatable_collection_rejects_nonupdatables(): +def test_updatable_collection_rejects_nonupdatables() -> None: coll: UpdatableCollection[Updatable] = UpdatableCollection() assert len(coll) == 0 @@ -94,7 +94,7 @@ def test_updatable_collection_rejects_nonupdatables(): assert len(coll) == 0 -def test_updatable_collection_construction(): +def test_updatable_collection_construction() -> None: good_list = [SimpleUpdatable(), SimpleUpdatable()] good = UpdatableCollection(good_list) assert len(good) == 2 @@ -104,7 +104,7 @@ def test_updatable_collection_construction(): _ = UpdatableCollection(bad_list) # pylint: disable-msg=W0612 -def test_updatable_collection_insertion(): +def test_updatable_collection_insertion() -> None: x = UpdatableCollection([MinimalUpdatable()]) assert len(x) == 1 assert isinstance(x[0], MinimalUpdatable) @@ -117,7 +117,7 @@ def test_updatable_collection_insertion(): x[0] = 1 -def test_updatable_collection_is_updated(): +def test_updatable_collection_is_updated() -> None: obj: UpdatableCollection[Updatable] = UpdatableCollection([SimpleUpdatable()]) new_params = {"x": -1.0, "y": 5.5} @@ -126,13 +126,13 @@ def test_updatable_collection_is_updated(): assert obj.is_updated() -def test_updatablecollection_without_derived_parameters(): +def test_updatablecollection_without_derived_parameters() -> None: obj: UpdatableCollection[Updatable] = UpdatableCollection() assert obj.get_derived_parameters() is None -def test_updatablecollection_with_items_without_derived_parameters(): +def test_updatablecollection_with_items_without_derived_parameters() -> None: """get_derived_parameters returns None when all items have no derived parameters.""" obj: UpdatableCollection[Updatable] = UpdatableCollection() @@ -155,7 +155,7 @@ def test_updatablecollection_with_items_without_derived_parameters(): assert obj.get_derived_parameters() is None -def test_collection_update_already_updated(): +def test_collection_update_already_updated() -> None: """Test calling update() twice on an UpdatableCollection. The second call should return early without updating items again @@ -186,7 +186,7 @@ def test_collection_update_already_updated(): assert obj2.a == 3.0 -def test_collection_reset_with_items(): +def test_collection_reset_with_items() -> None: """Test reset() on an UpdatableCollection with items. Verifies that reset() sets _updated to False and calls reset() on all diff --git a/tests/updatable/test_interface.py b/tests/updatable/test_interface.py index 91b69123a..fc29a9c2f 100644 --- a/tests/updatable/test_interface.py +++ b/tests/updatable/test_interface.py @@ -12,7 +12,7 @@ from tests.updatable.updatable_test_support import SimpleUpdatable -def test_warn_override(): +def test_warn_override() -> None: BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} ) @@ -22,7 +22,7 @@ def test_warn_override(): assert any("update" in str(r.message) for r in record) -def test_raise_override(): +def test_raise_override() -> None: BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} ) @@ -31,7 +31,7 @@ def test_raise_override(): assert_updatable_interface(obj, raise_on_override=True) -def test_collection_items(): +def test_collection_items() -> None: BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} ) @@ -40,7 +40,7 @@ def test_collection_items(): assert_updatable_interface(coll) -def test_collection_override(): +def test_collection_override() -> None: """Ensure overriding UpdatableCollection methods is detected.""" # create a subclass of UpdatableCollection that overrides 'update' BadCollection = type( @@ -52,7 +52,7 @@ def test_collection_override(): assert_updatable_interface(coll) -def test_recurses_attributes(): +def test_recurses_attributes() -> None: BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} ) @@ -63,7 +63,7 @@ def test_recurses_attributes(): assert_updatable_interface(parent) -def test_exact_single_method_override(): +def test_exact_single_method_override() -> None: """Assert the warning lists the single overridden method exactly.""" BadUpdatable = type("BadUpdatable", (Updatable,), {"reset": lambda self: None}) obj = BadUpdatable() @@ -73,7 +73,7 @@ def test_exact_single_method_override(): assert "['reset']" in str(record[0].message) -def test_exact_multiple_method_override(): +def test_exact_multiple_method_override() -> None: """Assert the warning lists multiple overridden methods in the defined order.""" impl = { "update": (lambda self, params: None), @@ -87,7 +87,7 @@ def test_exact_multiple_method_override(): assert "['update', 'required_parameters']" in str(record[0].message) -def test_collection_exact_methods(): +def test_collection_exact_methods() -> None: """Ensure UpdatableCollection overrides are reported with exact methods.""" impl = {"reset": (lambda self: None), "get_derived_parameters": (lambda self: None)} BadCollection = type("BadCollection", (UpdatableCollection,), impl) @@ -97,7 +97,7 @@ def test_collection_exact_methods(): assert "['reset', 'get_derived_parameters']" in str(record[0].message) -def test_recursive_flag_prevents_recursion(): +def test_recursive_flag_prevents_recursion() -> None: """If recursive=False, child updatables should not be checked.""" BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} @@ -113,7 +113,7 @@ def test_recursive_flag_prevents_recursion(): assert len(w) == 0 -def test_collection_recursive_flag_prevents_recursion(): +def test_collection_recursive_flag_prevents_recursion() -> None: """If recursive=False, items inside an UpdatableCollection should not be checked.""" BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} @@ -127,7 +127,7 @@ def test_collection_recursive_flag_prevents_recursion(): assert len(w) == 0 -def test_collection_recursive_raise_on_override(): +def test_collection_recursive_raise_on_override() -> None: BadUpdatable = type( "BadUpdatable", (Updatable,), {"update": lambda self, params: None} ) @@ -137,7 +137,7 @@ def test_collection_recursive_raise_on_override(): assert_updatable_interface(coll, recursive=True, raise_on_override=True) -def test_non_updatable_input_raises_typeerror(): +def test_non_updatable_input_raises_typeerror() -> None: """Passing a non-Updatable and non-UpdatableCollection should raise TypeError.""" with pytest.raises(TypeError): assert_updatable_interface(42) # type: ignore diff --git a/tests/updatable/test_records.py b/tests/updatable/test_records.py index 1a115fccd..820707246 100644 --- a/tests/updatable/test_records.py +++ b/tests/updatable/test_records.py @@ -9,7 +9,7 @@ ) -def test_updatable_reports(): +def test_updatable_reports() -> None: su = SimpleUpdatable("bob") mu = MinimalUpdatable("larry") su.mu = mu # pylint: disable=attribute-defined-outside-init @@ -27,7 +27,7 @@ def test_updatable_reports(): assert "Sampler parameters used: ['a']" in after_use -def test_updatable_record_single(): +def test_updatable_record_single() -> None: """Test record creation for a single Updatable without nesting.""" obj = SimpleUpdatable("test") params = ParamsMap({"test_x": 1.0, "test_y": 2.0}) @@ -44,7 +44,7 @@ def test_updatable_record_single(): assert len(record.child_records) == 0 -def test_updatable_record_with_internal_params(): +def test_updatable_record_with_internal_params() -> None: """Test record creation with both sampler and internal parameters.""" obj = SimpleUpdatable("test") obj.set_internal_parameter( @@ -67,7 +67,7 @@ def test_updatable_record_with_internal_params(): assert sorted(record.internal_params) == ["internal1", "internal2"] -def test_updatable_record_nested(): +def test_updatable_record_nested() -> None: """Test record creation for nested Updatable objects.""" parent = SimpleUpdatable("parent") child = MinimalUpdatable("child") @@ -91,7 +91,7 @@ def test_updatable_record_nested(): assert child_record.sampler_params == ["a"] -def test_updatable_record_complex_hierarchy(): +def test_updatable_record_complex_hierarchy() -> None: """Test record creation for a complex hierarchy of nested objects.""" root = SimpleUpdatable("root") branch1 = MinimalUpdatable("branch1") @@ -142,7 +142,7 @@ def test_updatable_record_complex_hierarchy(): assert leaf2_record.cls == "UpdatableWithDerived" -def test_updatable_record_empty_params(): +def test_updatable_record_empty_params() -> None: """Test record creation for an object with no parameters.""" class EmptyUpdatable(Updatable): @@ -160,7 +160,7 @@ class EmptyUpdatable(Updatable): assert len(record.internal_params) == 0 -def test_updatable_usage_record_empty_and_print_empty(): +def test_updatable_usage_record_empty_and_print_empty() -> None: rec = UpdatableUsageRecord( cls="EmptyUpdatable", prefix="pfx", @@ -181,7 +181,7 @@ def test_updatable_usage_record_empty_and_print_empty(): assert lines == ["EmptyUpdatable(pfx): "] -def test_updatable_usage_record_parent_collapses_and_printing(): +def test_updatable_usage_record_parent_collapses_and_printing() -> None: child = UpdatableUsageRecord( cls="Child", prefix="c", @@ -208,7 +208,7 @@ def test_updatable_usage_record_parent_collapses_and_printing(): ] -def test_updatable_usage_record_indent_and_child_recursion(): +def test_updatable_usage_record_indent_and_child_recursion() -> None: child = UpdatableUsageRecord( cls="Child", prefix="c", @@ -239,7 +239,7 @@ def test_updatable_usage_record_indent_and_child_recursion(): assert lines == expected -def test_updatable_usage_record_empty_child_and_print_empty_options(): +def test_updatable_usage_record_empty_child_and_print_empty_options() -> None: # child is empty child = UpdatableUsageRecord( cls="Child", @@ -266,7 +266,7 @@ def test_updatable_usage_record_empty_child_and_print_empty_options(): assert lines == ["Parent(p) => Child(c): "] -def test_updatable_usage_record_already_updated_flag(): +def test_updatable_usage_record_already_updated_flag() -> None: """If an UpdatableUsageRecord indicates it was already updated, the get_log_lines should return a single line noting it was already updated. """ @@ -284,7 +284,7 @@ def test_updatable_usage_record_already_updated_flag(): assert lines == ["Already: (already updated)"] -def test_updatable_usage_record_internal_params_only(): +def test_updatable_usage_record_internal_params_only() -> None: """If sampler_params is empty but internal_params is non-empty, is_empty should be False and get_log_lines should show the internal parameters line (this covers the second early-return branch). diff --git a/tests/updatable/test_utils.py b/tests/updatable/test_utils.py index d853be93e..9823a0931 100644 --- a/tests/updatable/test_utils.py +++ b/tests/updatable/test_utils.py @@ -4,7 +4,7 @@ from tests.updatable.updatable_test_support import MinimalUpdatable, SimpleUpdatable -def test_get_default_params_empty_args(): +def test_get_default_params_empty_args() -> None: """Test get_default_params() with no arguments. Should return an empty dictionary (lines 19-23 in _utils.py). @@ -14,7 +14,7 @@ def test_get_default_params_empty_args(): assert isinstance(result, dict) -def test_get_default_params_map_empty_args(): +def test_get_default_params_map_empty_args() -> None: """Test get_default_params_map() with no arguments. Should return an empty ParamsMap (lines 32-33 in _utils.py). @@ -24,7 +24,7 @@ def test_get_default_params_map_empty_args(): assert len(result.keys()) == 0 -def test_get_default_params_with_multiple_updatables(): +def test_get_default_params_with_multiple_updatables() -> None: """Test get_default_params() with multiple updatables. Verifies that all default values are collected correctly. diff --git a/tests/updatable/updatable_test_support.py b/tests/updatable/updatable_test_support.py index 49f73f776..dd8612d64 100644 --- a/tests/updatable/updatable_test_support.py +++ b/tests/updatable/updatable_test_support.py @@ -29,7 +29,7 @@ def __init__(self, prefix: str | None = None): class UpdatableWithDerived(Updatable): """A concrete type that implements Updatable that implements derived parameters.""" - def __init__(self): + def __init__(self) -> None: """Initialize object with defaulted values.""" super().__init__()