Skip to content

Commit

Permalink
update af_st and add helpers.py
Browse files Browse the repository at this point in the history
- Reduce the number of lines in each test by setting hypothesis settings
by defining callable functions in helpers.py
- Simplify generation of valid characters by adding strategy for
  generating valid characters
  • Loading branch information
kaitj committed Sep 11, 2023
1 parent fdcc93c commit cc6e1fd
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 174 deletions.
12 changes: 0 additions & 12 deletions afids_utils/afids.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,6 @@ class AfidPosition:
z: float = attrs.field()
desc: str = attrs.field(validator=_validate_desc)

def __sub__(self, other: AfidPosition) -> tuple[float, float, float]:
# Check if computation performed on corresponding AFIDs
if not self.label == other.label or not self.desc == other.desc:
raise ValueError("Non-corresponding AFIDs")

# Compute distances
x_dist = self.x - other.x
y_dist = self.y - other.y
z_dist = self.z - other.z

return x_dist, y_dist, z_dist


def _validate_afids(
instance: AfidSet,
Expand Down
32 changes: 0 additions & 32 deletions afids_utils/metrics.py

This file was deleted.

24 changes: 24 additions & 0 deletions afids_utils/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import annotations

from datetime import timedelta
from typing import Callable, TypeVar

from hypothesis import HealthCheck, settings

_T = TypeVar("_T")


def allow_function_scoped(callable: _T, /) -> _T:
"""Allow function_scoped fixtures in tests"""
return settings(
suppress_health_check=[HealthCheck.function_scoped_fixture]
)(callable)


def deadline(time: int | float | timedelta | None) -> Callable[[_T], _T]:
"""Change hypothesis deadline"""

def inner(callable: _T, /) -> _T:
return settings(deadline=time)(callable)

return inner
8 changes: 8 additions & 0 deletions afids_utils/tests/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
HUMAN_PROTOCOL_MAP = json.load(json_fpath)["human"]


def gen_chars():
return st.text(
min_size=2,
max_size=5,
alphabet=st.characters(min_codepoint=ord("A"), max_codepoint=ord("z")),
)


def valid_labels():
return st.integers(min_value=1, max_value=32)

Expand Down
82 changes: 21 additions & 61 deletions afids_utils/tests/test_afids.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
from pathlib import Path

import pytest
from hypothesis import HealthCheck, assume, example, given, settings
from hypothesis import assume, example, given
from hypothesis import strategies as st
from more_itertools import pairwise

import afids_utils.tests.strategies as af_st
from afids_utils.afids import AfidPosition, AfidSet
from afids_utils.exceptions import InvalidFiducialError, InvalidFileError
from afids_utils.tests.helpers import allow_function_scoped


@pytest.fixture
Expand Down Expand Up @@ -158,10 +159,8 @@ def test_repeated_incomplete_afid_set(


class TestAfidsIO:
@given(label=st.integers(min_value=0, max_value=31))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(label=af_st.valid_labels())
@allow_function_scoped
def test_valid_load(
self,
valid_fcsv_file: PathLike[str],
Expand All @@ -177,25 +176,15 @@ def test_valid_load(
assert isinstance(afids_set.slicer_version, str)
assert isinstance(afids_set.coord_system, str)
assert isinstance(afids_set.afids, list)
assert isinstance(afids_set.afids[label], AfidPosition)
assert isinstance(afids_set.afids[label - 1], AfidPosition)

def test_invalid_fpath(self):
with pytest.raises(FileNotFoundError, match=".*does not exist"):
AfidSet.load("invalid/fpath.fcsv")

@given(
ext=st.text(
min_size=2,
max_size=5,
alphabet=st.characters(
min_codepoint=ord("A"), max_codepoint=ord("z")
),
)
)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
def test_invalid_ext(self, valid_fcsv_file: PathLike[str], ext: str):
@given(ext=af_st.gen_chars())
@allow_function_scoped
def test_invalid_ext(self, ext: str):
assume(not ext == "fcsv" or not ext == "json")

with tempfile.NamedTemporaryFile(
Expand Down Expand Up @@ -224,19 +213,8 @@ def test_invalid_label_range(self, valid_fcsv_file: PathLike[str]):
with pytest.raises(InvalidFileError, match="Unexpected number.*"):
AfidSet.load(out_fcsv_file.name)

@given(
label=st.integers(min_value=0, max_value=31),
desc=st.text(
min_size=2,
max_size=5,
alphabet=st.characters(
min_codepoint=ord("A"), max_codepoint=ord("z")
),
),
)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(label=af_st.valid_labels(), desc=af_st.gen_chars())
@allow_function_scoped
def test_invalid_desc(
self,
valid_fcsv_file: PathLike[str],
Expand All @@ -247,16 +225,16 @@ def test_invalid_desc(
assume(
desc
not in [
human_mappings[label]["desc"],
human_mappings[label]["acronym"],
human_mappings[label - 1]["desc"],
human_mappings[label - 1]["acronym"],
]
)

# Replace valid description with a mismatch
with open(valid_fcsv_file) as valid_fcsv:
fcsv_data = valid_fcsv.readlines()
fcsv_data[label + 3] = fcsv_data[label + 3].replace(
human_mappings[label]["desc"], desc
fcsv_data[label + 2] = fcsv_data[label + 2].replace(
human_mappings[label - 1]["desc"], desc
)

# Write to temp file
Expand All @@ -283,18 +261,8 @@ def test_valid_save(self, valid_fcsv_file: PathLike[str]):

assert Path(out_fcsv_file.name).exists()

@given(
ext=st.text(
min_size=2,
max_size=5,
alphabet=st.characters(
min_codepoint=ord("A"), max_codepoint=ord("z")
),
)
)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(ext=af_st.gen_chars())
@allow_function_scoped
def test_invalid_ext_save(self, valid_fcsv_file: PathLike[str], ext: str):
assume(not ext == "fcsv" or not ext == "json")

Expand All @@ -306,9 +274,7 @@ def test_invalid_ext_save(self, valid_fcsv_file: PathLike[str], ext: str):
afid_set.save(out_file.name)

@given(afid_set=af_st.afid_sets())
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@allow_function_scoped
def test_save_invalid_coord_system(self, afid_set: AfidSet):
afid_set.coord_system = "invalid"

Expand All @@ -323,9 +289,7 @@ def test_save_invalid_coord_system(self, afid_set: AfidSet):
@given(
afid_set=af_st.afid_sets(), coord_sys=st.sampled_from(["LPS", "RAS"])
)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@allow_function_scoped
def test_update_coord_system(self, afid_set: AfidSet, coord_sys: str):
afid_set.coord_system = coord_sys

Expand All @@ -348,10 +312,8 @@ def test_update_coord_system(self, afid_set: AfidSet, coord_sys: str):


class TestAfidsCore:
@given(label=st.integers(min_value=1, max_value=32))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(label=af_st.valid_labels())
@allow_function_scoped
def test_valid_get_afid(self, valid_fcsv_file: PathLike[str], label: int):
afid_set = AfidSet.load(valid_fcsv_file)
afid_pos = afid_set.get_afid(label)
Expand All @@ -360,9 +322,7 @@ def test_valid_get_afid(self, valid_fcsv_file: PathLike[str], label: int):
assert isinstance(afid_pos, AfidPosition)

@given(label=st.integers(min_value=-100, max_value=100))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@allow_function_scoped
def test_invalid_get_afid(
self, valid_fcsv_file: PathLike[str], label: int
):
Expand Down
46 changes: 14 additions & 32 deletions afids_utils/tests/test_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
from pathlib import Path

import pytest
from hypothesis import HealthCheck, assume, given, settings
from hypothesis import assume, given
from hypothesis import strategies as st

import afids_utils.tests.strategies as af_st
from afids_utils.afids import AfidPosition, AfidSet
from afids_utils.exceptions import InvalidFileError
from afids_utils.ext.fcsv import _get_afids, _get_metadata, save_fcsv
from afids_utils.tests.strategies import afid_sets
from afids_utils.tests.helpers import allow_function_scoped


@pytest.fixture
Expand All @@ -24,9 +25,7 @@ def valid_fcsv_file() -> PathLike[str]:

class TestLoadFcsv:
@given(coord_num=st.integers(min_value=0, max_value=1))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@allow_function_scoped
def test_get_valid_metadata(
self, valid_fcsv_file: PathLike[str], coord_num: int
):
Expand Down Expand Up @@ -62,9 +61,7 @@ def test_get_valid_metadata(
assert parsed_coord == "RAS"

@given(coord_num=st.integers(min_value=2))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@allow_function_scoped
def test_invalid_num_coord(
self, valid_fcsv_file: PathLike[str], coord_num: int
):
Expand All @@ -89,17 +86,8 @@ def test_invalid_num_coord(
):
_get_metadata(temp_in_fcsv.readlines())

@given(
coord_str=st.text(
min_size=3,
alphabet=st.characters(
min_codepoint=ord("A"), max_codepoint=ord("z")
),
)
)
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(coord_str=af_st.gen_chars())
@allow_function_scoped
def test_invalid_str_coord(
self, valid_fcsv_file: PathLike[str], coord_str: int
):
Expand Down Expand Up @@ -145,34 +133,28 @@ def test_invalid_header(self, valid_fcsv_file: PathLike[str]):
):
_get_metadata(temp_in_fcsv.readlines())

@given(label=st.integers(min_value=0, max_value=31))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(label=af_st.valid_labels())
@allow_function_scoped
def test_valid_get_afids(self, valid_fcsv_file: PathLike[str], label: int):
with open(valid_fcsv_file) as valid_fcsv:
afids_positions = _get_afids(valid_fcsv.readlines())

assert isinstance(afids_positions, list)
assert isinstance(afids_positions[label], AfidPosition)
assert isinstance(afids_positions[label - 1], AfidPosition)


class TestSaveFcsv:
@given(afid_set=afid_sets())
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(afid_set=af_st.afid_sets())
@allow_function_scoped
def test_save_fcsv_invalid_template(
self,
afid_set: AfidSet,
):
with pytest.raises(FileNotFoundError):
save_fcsv(afid_set, "/invalid/template/path.fcsv")

@given(afid_set=afid_sets(randomize_header=False))
@settings(
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
@given(afid_set=af_st.afid_sets(randomize_header=False))
@allow_function_scoped
def test_save_fcsv_valid_template(self, afid_set: AfidSet):
with tempfile.NamedTemporaryFile(
mode="w", prefix="sub-test_desc-", suffix="_afids.fcsv"
Expand Down
Loading

0 comments on commit cc6e1fd

Please sign in to comment.