Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/src/opentrons/protocol_api/_liquid_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def flow_rate(self) -> Optional[float]:

@flow_rate.setter
def flow_rate(self, new_flow_rate: float) -> None:
validated_flow_rate = validation.ensure_positive_float(new_flow_rate)
validated_flow_rate = validation.ensure_greater_than_zero_float(new_flow_rate)
self._flow_rate = validated_flow_rate

def _get_shared_data_params(self) -> Optional[SharedDataBlowoutParams]:
Expand Down
188 changes: 188 additions & 0 deletions api/tests/opentrons/protocol_api/test_lc_blowout_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
"""Tests for liquid class blowout properties in the Opentrons protocol API."""

from pydantic import ValidationError
import pytest
from typing import Any
from hypothesis import given, strategies as st, settings

from opentrons.protocol_api._liquid_properties import (
_build_blowout_properties,
)
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
BlowoutProperties,
BlowoutParams,
BlowoutLocation,
)

from . import (
boolean_looking_values,
invalid_values,
negative_or_zero_floats_and_ints,
positive_non_zero_floats_and_ints,
)


def test_blowout_properties_enable_and_disable() -> None:
"""Test that enable and disable work as expected."""
bp = _build_blowout_properties(
BlowoutProperties(
enable=False,
params=BlowoutParams(location=BlowoutLocation.DESTINATION, flowRate=100),
)
)
bp.enabled = True
assert bp.enabled is True
bp.enabled = False
assert bp.enabled is False


def test_blowout_properties_none_instantiation_combos() -> None:
"""Test that none values raise."""
with pytest.raises(ValidationError):
_build_blowout_properties(
BlowoutProperties(enable=None, params=BlowoutParams(location=None, flowRate=None)) # type: ignore
)
with pytest.raises(ValidationError):
_build_blowout_properties(
BlowoutProperties(enable=True, params=BlowoutParams(location=None, flowRate=100)) # type: ignore
)


@given(bad_enable=boolean_looking_values)
@settings(deadline=None, max_examples=50)
def test_blowout_properties_enabled_bad_values(bad_enable: Any) -> None:
"""Test that invalid enable values raise."""
with pytest.raises(ValidationError):
_build_blowout_properties(
BlowoutProperties(
enable=bad_enable,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)
bp = _build_blowout_properties(
BlowoutProperties(
enable=False,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)
with pytest.raises(ValueError):
bp.enabled = bad_enable


@given(good_flow_rate=positive_non_zero_floats_and_ints)
@settings(deadline=None, max_examples=50)
def test_blowout_properties_flow_rate(good_flow_rate: Any) -> None:
"""Test that valid flow rate values are accepted."""
_build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(
location=BlowoutLocation.DESTINATION, flowRate=good_flow_rate
),
)
)
bp = _build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=1),
)
)
bp.flow_rate = good_flow_rate
assert bp.flow_rate == float(good_flow_rate)


@given(bad_flow_rate=st.one_of(invalid_values, negative_or_zero_floats_and_ints))
@settings(deadline=None, max_examples=50)
def test_blowout_properties_flow_rate_bad_values(bad_flow_rate: Any) -> None:
"""Test that invalid flow rate values raise."""
with pytest.raises(ValidationError):
_build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(
location=BlowoutLocation.TRASH, flowRate=bad_flow_rate
),
)
)
bp = _build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)
with pytest.raises(ValueError):
bp.flow_rate = bad_flow_rate


@given(
good_location=st.one_of(
st.just(BlowoutLocation.DESTINATION),
st.just(BlowoutLocation.TRASH),
st.just(BlowoutLocation.SOURCE),
)
)
@settings(deadline=None, max_examples=50)
def test_blowout_properties_location_enum(good_location: Any) -> None:
"""Test that valid location values are accepted."""
bp = _build_blowout_properties(
BlowoutProperties(
enable=True, params=BlowoutParams(location=good_location, flowRate=50)
)
)
assert bp.location == good_location
bp = _build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)

bp.location = good_location
assert bp.location == good_location


@given(
good_location=st.one_of(
st.just("destination"),
st.just("trash"),
st.just("source"),
)
)
@settings(deadline=None, max_examples=50)
def test_blowout_properties_location_str(good_location: Any) -> None:
"""Test that valid location values are accepted."""
bp = _build_blowout_properties(
BlowoutProperties(
enable=True, params=BlowoutParams(location=good_location, flowRate=50)
)
)
assert bp.location is not None and bp.location.value == good_location
bp = _build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)
bp.location = good_location
assert bp.location is not None and bp.location.value == good_location


@given(bad_location=st.one_of(invalid_values, st.just("chute")))
@settings(deadline=None, max_examples=50)
def test_blowout_properties_location_bad(bad_location: Any) -> None:
"""Test that invalid location values raise."""
with pytest.raises(ValidationError):
bp = _build_blowout_properties(
BlowoutProperties(
enable=True, params=BlowoutParams(location=bad_location, flowRate=50)
)
)

bp = _build_blowout_properties(
BlowoutProperties(
enable=True,
params=BlowoutParams(location=BlowoutLocation.TRASH, flowRate=50),
)
)
with pytest.raises(ValueError):
bp.location = bad_location
4 changes: 2 additions & 2 deletions shared-data/command/schemas/12.json
Original file line number Diff line number Diff line change
Expand Up @@ -576,11 +576,11 @@
"flowRate": {
"anyOf": [
{
"minimum": 0,
"exclusiveMinimum": 0,
"type": "integer"
},
{
"minimum": 0.0,
"exclusiveMinimum": 0.0,
"type": "number"
}
],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oo this is confusing. We have a BlowOutParams and a BlowoutParams 🙈

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,15 @@ class BlowoutParams(BaseModel):
location: BlowoutLocation = Field(
..., description="Location well or trash entity for blow out."
)
flowRate: _NonNegativeNumber = Field(
flowRate: _GreaterThanZeroNumber = Field(
..., description="Flow rate for blow out, in microliters per second."
)


class BlowoutProperties(BaseModel):
"""Blowout properties."""

enable: bool = Field(..., description="Whether blow-out is enabled.")
enable: StrictBool = Field(..., description="Whether blow-out is enabled.")
params: BlowoutParams | SkipJsonSchema[None] = Field(
None,
description="Parameters for the blowout function.",
Expand Down