Skip to content

Commit

Permalink
added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sanni-t committed Jan 9, 2025
1 parent ab5db25 commit 47b8f91
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 34 deletions.
30 changes: 20 additions & 10 deletions api/src/opentrons/protocol_api/core/engine/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,13 @@ def _pick_up_tip() -> None:
# TODO: make sure that the tip has air gap when moving to the trash
_drop_tip()

post_disp_tip_contents = [
tx_comps_executor.LiquidAndAirGapPair(
liquid=45,
air_gap=67,
)
]

def _get_location_and_well_core_from_next_tip_info(
self,
tip_info: NextTipInfo,
Expand Down Expand Up @@ -1107,6 +1114,7 @@ def aspirate_liquid_class(
transfer_type: tx_comps_executor.TransferType,
tip_contents: List[tx_comps_executor.LiquidAndAirGapPair],
) -> List[tx_comps_executor.LiquidAndAirGapPair]:
print("!!!!", tip_contents)
"""Execute aspiration steps.
1. Submerge
Expand All @@ -1119,6 +1127,7 @@ def aspirate_liquid_class(
Return: List of liquid and air gap pairs in tip.
"""
aspirate_props = transfer_properties.aspirate
_tip_contents = tip_contents.copy()
tx_commons.check_valid_volume_parameters(
disposal_volume=0, # No disposal volume for 1-to-1 transfer
air_gap=aspirate_props.retract.air_gap_by_volume.get_for_volume(volume),
Expand All @@ -1133,14 +1142,14 @@ def aspirate_liquid_class(
)
)
aspirate_location = Location(aspirate_point, labware=source_loc.labware)
if len(tip_contents) > 0:
last_liquid_and_airgap_in_tip = tip_contents[-1]
if len(_tip_contents) > 0:
last_liquid_and_airgap_in_tip = _tip_contents[-1]
else:
last_liquid_and_airgap_in_tip = tx_comps_executor.LiquidAndAirGapPair(
liquid=0,
air_gap=0,
)
tip_contents = [last_liquid_and_airgap_in_tip]
_tip_contents = [last_liquid_and_airgap_in_tip]
components_executor = tx_comps_executor.TransferComponentsExecutor(
instrument_core=self,
transfer_properties=transfer_properties,
Expand All @@ -1163,8 +1172,8 @@ def aspirate_liquid_class(
components_executor.aspirate_and_wait(volume=volume)
components_executor.retract_after_aspiration(volume=volume)
last_contents = components_executor.tip_state.last_liquid_and_air_gap_in_tip
tip_contents[-1] = last_contents
return tip_contents
_tip_contents[-1] = last_contents
return _tip_contents

def dispense_liquid_class(
self,
Expand Down Expand Up @@ -1208,6 +1217,7 @@ def dispense_liquid_class(
"""
dispense_props = transfer_properties.dispense
dest_loc, dest_well = dest
_tip_contents = tip_contents.copy()
dispense_point = (
tx_comps_executor.absolute_point_from_position_reference_and_offset(
well=dest_well,
Expand All @@ -1216,14 +1226,14 @@ def dispense_liquid_class(
)
)
dispense_location = Location(dispense_point, labware=dest_loc.labware)
if len(tip_contents) > 0:
last_liquid_and_airgap_in_tip = tip_contents[-1]
if len(_tip_contents) > 0:
last_liquid_and_airgap_in_tip = _tip_contents[-1]
else:
last_liquid_and_airgap_in_tip = tx_comps_executor.LiquidAndAirGapPair(
liquid=0,
air_gap=0,
)
tip_contents = [last_liquid_and_airgap_in_tip]
_tip_contents = [last_liquid_and_airgap_in_tip]
components_executor = tx_comps_executor.TransferComponentsExecutor(
instrument_core=self,
transfer_properties=transfer_properties,
Expand Down Expand Up @@ -1254,8 +1264,8 @@ def dispense_liquid_class(
source_well=source[1] if source else None,
)
last_contents = components_executor.tip_state.last_liquid_and_air_gap_in_tip
tip_contents[-1] = last_contents
return tip_contents
_tip_contents[-1] = last_contents
return _tip_contents

def retract(self) -> None:
"""Retract this instrument to the top of the gantry."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ def retract_after_dispensing(
blowout_props.enabled
and blowout_props.location != BlowoutLocation.DESTINATION
):
# TODO: no-op touch tip if touch tip is enabled and blowout is in trash/ reservoir/ any labware with touch-tip disabled
assert blowout_props.flow_rate is not None
self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
touch_tip_and_air_gap_location: Optional[Location]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
"""Tests for the transfer APIs using liquid classes."""
import pytest
import mock
from decoy import Decoy
from opentrons_shared_data.robot.types import RobotTypeEnum

from opentrons.protocol_api import ProtocolContext
from opentrons.config import feature_flags as ff
from opentrons.protocol_api.core.engine import InstrumentCore
from opentrons.protocol_api.core.engine.transfer_components_executor import (
TransferType,
LiquidAndAirGapPair,
)


@pytest.mark.ot3_only
@pytest.mark.parametrize(
"simulated_protocol_context", [("2.20", "Flex")], indirect=True
)
def test_water_transfer(
def test_water_transfer_with_volume_more_than_tip_max(
decoy: Decoy, mock_feature_flags: None, simulated_protocol_context: ProtocolContext
) -> None:
"""It should run the transfer steps without any errors.
Expand All @@ -36,29 +42,194 @@ def test_water_transfer(
)

water = simulated_protocol_context.define_liquid_class("water")
pipette_50.transfer_liquid(
liquid_class=water,
volume=60,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="always",
trash_location=trash,
with mock.patch.object(
InstrumentCore,
"pick_up_tip",
side_effect=InstrumentCore.pick_up_tip,
autospec=True,
) as patched_pick_up_tip:
mock_manager = mock.Mock()
mock_manager.attach_mock(patched_pick_up_tip, "pick_up_tip")

pipette_50.transfer_liquid(
liquid_class=water,
volume=60,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="always",
trash_location=trash,
)
assert patched_pick_up_tip.call_count == 24
patched_pick_up_tip.reset_mock()

pipette_50.transfer_liquid(
liquid_class=water,
volume=100,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="per source",
trash_location=trash,
)
assert patched_pick_up_tip.call_count == 12
patched_pick_up_tip.reset_mock()

pipette_50.pick_up_tip()
pipette_50.transfer_liquid(
liquid_class=water,
volume=50,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="never",
trash_location=trash,
)
pipette_50.drop_tip()
assert patched_pick_up_tip.call_count == 1


@pytest.mark.ot3_only
@pytest.mark.parametrize(
"simulated_protocol_context", [("2.20", "Flex")], indirect=True
)
def test_order_of_water_transfer_steps(
decoy: Decoy, mock_feature_flags: None, simulated_protocol_context: ProtocolContext
) -> None:
"""It should run the transfer steps without any errors.
This test only checks that various supported configurations for a transfer
analyze successfully. It doesn't check whether the steps are as expected.
That will be covered in analysis snapshot tests.
"""
decoy.when(ff.allow_liquid_classes(RobotTypeEnum.FLEX)).then_return(True)
trash = simulated_protocol_context.load_trash_bin("A3")
tiprack = simulated_protocol_context.load_labware(
"opentrons_flex_96_tiprack_50ul", "D1"
)
pipette_50.transfer_liquid(
liquid_class=water,
volume=60,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="per source",
trash_location=trash,
pipette_50 = simulated_protocol_context.load_instrument(
"flex_1channel_50", mount="left", tip_racks=[tiprack]
)
pipette_50.pick_up_tip()
pipette_50.transfer_liquid(
liquid_class=water,
volume=50,
source=nest_plate.rows()[0],
dest=arma_plate.rows()[0],
new_tip="never",
trash_location=trash,
nest_plate = simulated_protocol_context.load_labware(
"nest_96_wellplate_200ul_flat", "C3"
)
pipette_50.drop_tip()
arma_plate = simulated_protocol_context.load_labware(
"armadillo_96_wellplate_200ul_pcr_full_skirt", "C2"
)

water = simulated_protocol_context.define_liquid_class("water")
with (
mock.patch.object(
InstrumentCore,
"load_liquid_class",
side_effect=InstrumentCore.load_liquid_class,
autospec=True,
) as patched_load_liquid_class,
mock.patch.object(
InstrumentCore,
"pick_up_tip",
side_effect=InstrumentCore.pick_up_tip,
autospec=True,
) as patched_pick_up_tip,
mock.patch.object(
InstrumentCore,
"aspirate_liquid_class",
side_effect=InstrumentCore.aspirate_liquid_class,
autospec=True,
) as patched_aspirate,
mock.patch.object(
InstrumentCore,
"dispense_liquid_class",
side_effect=InstrumentCore.dispense_liquid_class,
autospec=True,
) as patched_dispense,
mock.patch.object(
InstrumentCore,
"drop_tip_in_disposal_location",
side_effect=InstrumentCore.drop_tip_in_disposal_location,
autospec=True,
) as patched_drop_tip,
):
mock_manager = mock.Mock()
mock_manager.attach_mock(patched_pick_up_tip, "pick_up_tip")
mock_manager.attach_mock(patched_load_liquid_class, "load_liquid_class")
mock_manager.attach_mock(patched_aspirate, "aspirate_liquid_class")
mock_manager.attach_mock(patched_dispense, "dispense_liquid_class")
mock_manager.attach_mock(patched_drop_tip, "drop_tip_in_disposal_location")
pipette_50.transfer_liquid(
liquid_class=water,
volume=40,
source=nest_plate.rows()[0][:2],
dest=arma_plate.rows()[0][:2],
new_tip="always",
trash_location=trash,
)
expected_calls = [
mock.call.load_liquid_class(
mock.ANY,
name="water",
transfer_properties=mock.ANY,
tiprack_uri="opentrons/opentrons_flex_96_tiprack_50ul/1",
),
mock.call.pick_up_tip(
mock.ANY,
location=mock.ANY,
well_core=mock.ANY,
presses=mock.ANY,
increment=mock.ANY,
),
mock.call.aspirate_liquid_class(
mock.ANY,
volume=40,
source=mock.ANY,
transfer_properties=mock.ANY,
transfer_type=TransferType.ONE_TO_ONE,
tip_contents=[LiquidAndAirGapPair(liquid=0, air_gap=0)],
),
mock.call.dispense_liquid_class(
mock.ANY,
volume=40,
dest=mock.ANY,
source=mock.ANY,
transfer_properties=mock.ANY,
transfer_type=TransferType.ONE_TO_ONE,
tip_contents=[LiquidAndAirGapPair(liquid=0, air_gap=0.1)],
trash_location=mock.ANY,
),
mock.call.drop_tip_in_disposal_location(
mock.ANY,
disposal_location=trash,
home_after=False,
alternate_tip_drop=True,
),
mock.call.pick_up_tip(
mock.ANY,
location=mock.ANY,
well_core=mock.ANY,
presses=mock.ANY,
increment=mock.ANY,
),
mock.call.aspirate_liquid_class(
mock.ANY,
volume=40,
source=mock.ANY,
transfer_properties=mock.ANY,
transfer_type=TransferType.ONE_TO_ONE,
tip_contents=[LiquidAndAirGapPair(liquid=0, air_gap=0)],
),
mock.call.dispense_liquid_class(
mock.ANY,
volume=40,
dest=mock.ANY,
source=mock.ANY,
transfer_properties=mock.ANY,
transfer_type=TransferType.ONE_TO_ONE,
tip_contents=[LiquidAndAirGapPair(liquid=40, air_gap=0.1)],
trash_location=mock.ANY,
),
mock.call.drop_tip_in_disposal_location(
mock.ANY,
disposal_location=trash,
home_after=False,
alternate_tip_drop=True,
),
]
assert len(mock_manager.mock_calls) == 9
assert mock_manager.mock_calls == expected_calls

0 comments on commit 47b8f91

Please sign in to comment.