diff --git a/api/tests/opentrons/containers/test_containers.py b/api/tests/opentrons/containers/test_containers.py index 5a046bf0c19..b6f19095cb3 100644 --- a/api/tests/opentrons/containers/test_containers.py +++ b/api/tests/opentrons/containers/test_containers.py @@ -1,12 +1,12 @@ import math -import unittest + +import pytest from opentrons.legacy_api.containers import ( load as containers_load, list as containers_list, load_new_labware as new_load ) -from opentrons.legacy_api.robot import Robot from opentrons.legacy_api.containers.placeable import ( Container, Well, @@ -21,202 +21,200 @@ # TODO: Modify calls that expect Deck and Slot to be Placeables -class ContainerTestCase(unittest.TestCase): - def setUp(self): - self.robot = Robot() - - def tearDown(self): - del self.robot - - def test_load_same_slot_force(self): - container_name = '96-flat' - slot = '1' - containers_load(self.robot, container_name, slot) - # 2018-1-30 Incremented number of containers based on fixed trash - self.assertEqual(len(self.robot.get_containers()), 2) - - self.assertRaises( - RuntimeWarning, containers_load, - self.robot, container_name, slot) - self.assertRaises( - RuntimeWarning, containers_load, - self.robot, container_name, slot, share=True) - self.assertRaises( - RuntimeWarning, containers_load, - self.robot, container_name, slot, 'custom-name') - self.assertRaises( - RuntimeWarning, containers_load, - self.robot, 'trough-12row', slot) - self.assertRaises( - RuntimeWarning, containers_load, - self.robot, 'trough-12row', slot, 'custom-name') - - containers_load( - self.robot, container_name, slot, 'custom-name', share=True) - self.assertEqual(len(self.robot.get_containers()), 3) - - containers_load( - self.robot, 'trough-12row', slot, share=True) - self.assertEqual(len(self.robot.get_containers()), 4) - - def test_load_legacy_slot_names(self): - slots_old = [ - 'A1', 'B1', 'C1', - 'A2', 'B2', 'C2', - 'A3', 'B3', 'C3', - 'A4', 'B4', 'C4' - ] - slots_new = [ - '1', '2', '3', - '4', '5', '6', - '7', '8', '9', - '10', '11', '12' - ] - import warnings - warnings.filterwarnings('ignore') - - # Only check up to the non fixed-trash slots - def test_slot_name(slot_name, expected_name): - self.robot.reset() - p = containers_load(self.robot, '96-flat', slot_name) - slot_name = p.get_parent().get_name() - assert slot_name == expected_name - - for i in range(len(slots_old) - 1): - test_slot_name(slots_new[i], slots_new[i]) - test_slot_name(int(slots_new[i]), slots_new[i]) - test_slot_name(slots_old[i], slots_new[i]) - - warnings.filterwarnings('default') - - def test_new_slot_names(self): - trough = 'usascientific_12_reservoir_22ml' - plate = 'generic_96_wellplate_340ul_flat' - tuberack = 'opentrons_6_tuberack_falcon_50ml_conical' - - cont = new_load(trough) - self.assertTrue(isinstance(cont, Container)) - cont = new_load(plate) - self.assertTrue(isinstance(cont, Container)) - cont = new_load(tuberack) - self.assertTrue(isinstance(cont, Container)) - - def test_load_new_trough(self): - trough = 'usascientific_12_reservoir_22ml' - cont = new_load(trough) - self.assertEqual(cont.size(), (0, 0, 0)) - self.assertEqual( - cont.wells('A1')._coordinates, (13.94 - 4.165, 42.9 + 35.94, 2.29)) - - def test_containers_list(self): - res = containers_list() - self.assertTrue(len(res)) - - def test_bad_unpack_containers(self): - self.assertRaises( - ValueError, unpack_location, 1) - - def test_iterate_without_parent(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - self.assertRaises( - Exception, next, c) - - def test_back_container_getitem(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - self.assertRaises(TypeError, c.__getitem__, (1, 1)) - - def test_iterator(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - res = [well.coordinates() for well in c] - expected = [(0, 0, 0), (5, 0, 0), (0, 5, 0), (5, 5, 0)] - - self.assertListEqual(res, expected) - - def test_next(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - well = c['A1'] - expected = c.get_child_by_name('B1') - - self.assertEqual(next(well), expected) - - def test_int_index(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - - self.assertEqual(c[3], c.get_child_by_name('B2')) - self.assertEqual(c[1], c.get_child_by_name('B1')) - - def test_named_well(self): - deck = Deck() - slot = Slot() - c = Container() - deck.add(slot, 'A1', (0, 0, 0)) - red = Well(properties={'radius': 5}) - blue = Well(properties={'radius': 5}) - c.add(red, "Red", (0, 0, 0)) - c.add(blue, "Blue", (10, 0, 0)) - slot.add(c) - - self.assertEqual(deck['A1'][0]['Red'], red) - - def test_generate_plate(self): - c = generate_plate( - wells=96, - cols=8, - spacing=(10, 15), - offset=(5, 15), - radius=5 - ) - - self.assertEqual(c['A1'].coordinates(), (5, 15, 0)) - self.assertEqual(c['B2'].coordinates(), (15, 30, 0)) - - def test_coordinates(self): - deck = Deck() - slot = Slot() - plate = generate_plate( - wells=96, - cols=8, - spacing=(10, 15), - offset=(5, 15), - radius=5 - ) - deck.add(slot, 'B2', (100, 200, 0)) - slot.add(plate) - - self.assertEqual(plate['A1'].coordinates(deck), (105, 215, 0)) - - def test_get_name(self): - deck = Deck() - slot = Slot() - c = Container() - deck.add(slot, 'A1', (0, 0, 0)) - red = Well(properties={'radius': 5}) - blue = Well(properties={'radius': 5}) - c.add(red, "Red", (0, 0, 0)) - c.add(blue, "Blue", (10, 0, 0)) - slot.add(c) - - self.assertEqual(red.get_name(), 'Red') - - def test_well_from_center(self): - deck = Deck() - slot = Slot() - plate = generate_plate( - wells=4, - cols=2, - spacing=(10, 10), - offset=(0, 0), - radius=5 - ) - deck.add(slot, 'A1', (0, 0, 0)) - slot.add(plate) - - self.assertEqual( - plate['B2'].center(), - (5, 5, 0)) - self.assertEqual( - plate['B2'].from_center(x=0.0, y=0.0, z=0.0), - (5, 5, 0)) - self.assertEqual( - plate['B2'].from_center(r=1.0, theta=math.pi / 2, h=0.0), - (5.0, 10.0, 0.0)) +def test_load_same_slot_force(robot): + container_name = '96-flat' + slot = '1' + containers_load(robot, container_name, slot) + # 2018-1-30 Incremented number of containers based on fixed trash + assert len(robot.get_containers()) == 2 + + with pytest.raises(RuntimeWarning): + containers_load(robot, container_name, slot) + with pytest.raises(RuntimeWarning): + containers_load(robot, container_name, slot) + with pytest.raises(RuntimeWarning): + containers_load(robot, container_name, slot) + with pytest.raises(RuntimeWarning): + containers_load(robot, container_name, slot) + with pytest.raises(RuntimeWarning): + containers_load(robot, container_name, slot) + + containers_load( + robot, container_name, slot, 'custom-name', share=True) + assert len(robot.get_containers()) == 3 + + containers_load( + robot, 'trough-12row', slot, share=True) + assert len(robot.get_containers()) == 4 + + +def test_load_legacy_slot_names(robot): + slots_old = [ + 'A1', 'B1', 'C1', + 'A2', 'B2', 'C2', + 'A3', 'B3', 'C3', + 'A4', 'B4', 'C4' + ] + slots_new = [ + '1', '2', '3', + '4', '5', '6', + '7', '8', '9', + '10', '11', '12' + ] + import warnings + warnings.filterwarnings('ignore') + + # Only check up to the non fixed-trash slots + def test_slot_name(slot_name, expected_name): + robot.reset() + p = containers_load(robot, '96-flat', slot_name) + slot_name = p.get_parent().get_name() + assert slot_name == expected_name + + for i in range(len(slots_old) - 1): + test_slot_name(slots_new[i], slots_new[i]) + test_slot_name(int(slots_new[i]), slots_new[i]) + test_slot_name(slots_old[i], slots_new[i]) + + warnings.filterwarnings('default') + + +def test_new_slot_names(robot): + trough = 'usascientific_12_reservoir_22ml' + plate = 'generic_96_wellplate_340ul_flat' + tuberack = 'opentrons_6_tuberack_falcon_50ml_conical' + + cont = new_load(trough) + assert isinstance(cont, Container) + cont = new_load(plate) + assert isinstance(cont, Container) + cont = new_load(tuberack) + assert isinstance(cont, Container) + + +def test_load_new_trough(robot): + trough = 'usascientific_12_reservoir_22ml' + cont = new_load(trough) + assert cont.size() == (0, 0, 0) + assert cont.wells('A1')._coordinates \ + == (13.94 - 4.165, 42.9 + 35.94, 2.29) + + +def test_containers_list(robot): + res = containers_list() + assert res + + +def test_bad_unpack_containers(robot): + with pytest.raises(ValueError): + unpack_location(1) + + +def test_iterate_without_parent(robot): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + with pytest.raises(Exception): + next(c) + + +def test_back_container_getitem(robot): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + with pytest.raises(TypeError): + c.__getitem__((1, 1)) + + +def test_iterator(robot): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + res = [well.coordinates() for well in c] + expected = [(0, 0, 0), (5, 0, 0), (0, 5, 0), (5, 5, 0)] + assert res == expected + + +def test_next(robot): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + well = c['A1'] + expected = c.get_child_by_name('B1') + + assert next(well) == expected + + +def test_int_index(robot): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + + assert c[3] == c.get_child_by_name('B2') + assert c[1] == c.get_child_by_name('B1') + + +def test_named_well(robot): + deck = Deck() + slot = Slot() + c = Container() + deck.add(slot, 'A1', (0, 0, 0)) + red = Well(properties={'radius': 5}) + blue = Well(properties={'radius': 5}) + c.add(red, "Red", (0, 0, 0)) + c.add(blue, "Blue", (10, 0, 0)) + slot.add(c) + + assert deck['A1'][0]['Red'] == red + + +def test_generate_plate(robot): + c = generate_plate( + wells=96, + cols=8, + spacing=(10, 15), + offset=(5, 15), + radius=5 + ) + + assert c['A1'].coordinates() == (5, 15, 0) + assert c['B2'].coordinates() == (15, 30, 0) + + +def test_coordinates(robot): + deck = Deck() + slot = Slot() + plate = generate_plate( + wells=96, + cols=8, + spacing=(10, 15), + offset=(5, 15), + radius=5 + ) + deck.add(slot, 'B2', (100, 200, 0)) + slot.add(plate) + + assert plate['A1'].coordinates(deck) == (105, 215, 0) + + +def test_get_name(robot): + deck = Deck() + slot = Slot() + c = Container() + deck.add(slot, 'A1', (0, 0, 0)) + red = Well(properties={'radius': 5}) + blue = Well(properties={'radius': 5}) + c.add(red, "Red", (0, 0, 0)) + c.add(blue, "Blue", (10, 0, 0)) + slot.add(c) + + assert red.get_name() == 'Red' + + +def test_well_from_center(robot): + deck = Deck() + slot = Slot() + plate = generate_plate( + wells=4, + cols=2, + spacing=(10, 10), + offset=(0, 0), + radius=5 + ) + deck.add(slot, 'A1', (0, 0, 0)) + slot.add(plate) + + assert plate['B2'].center() == (5, 5, 0) + assert plate['B2'].from_center(x=0.0, y=0.0, z=0.0) == (5, 5, 0) + assert plate['B2'].from_center(r=1.0, theta=math.pi / 2, h=0.0)\ + == (5.0, 10.0, 0.0) diff --git a/api/tests/opentrons/containers/test_grid.py b/api/tests/opentrons/containers/test_grid.py index 5e0e1b46d4c..9ccaa7029f6 100644 --- a/api/tests/opentrons/containers/test_grid.py +++ b/api/tests/opentrons/containers/test_grid.py @@ -1,96 +1,84 @@ -import unittest +import pytest from opentrons.legacy_api.containers import load from opentrons.legacy_api.instruments import pipette -from opentrons.legacy_api.robot import Robot # TODO: Remove (ordering tested by new labware def test suite) -class GridTestCase(unittest.TestCase): - def setUp(self): - self.robot = Robot() - self.plate = load(self.robot, '96-flat', '4') - - def tearDown(self): - del self.robot - - def test_rows_cols(self): - plate = self.plate - wells = [ - plate.rows[1]['2'], - plate.rows['B']['2'], - plate.rows['B'][1], - plate.rows[1][1], - plate.cols['2']['B'], - plate.cols[1]['B'], - plate.cols[1][1], - plate['B2'], - plate[9] - ] - - for well, next_well in zip(wells[:-1], wells[1:]): - self.assertEqual(well, next_well) - - # TODO(artyom 20171031): uncomment once container storage and stabilized - # def test_placeable(self): - # plate = self.plate - # self.assertEqual(plate.rows[0].center(plate), (11.24, 14.34, 5.25)) - # self.assertEqual(plate.rows[1].center(plate), (11.24, 23.34, 5.25)) - # self.assertEqual(plate.rows[0].center(plate), - # plate.cols[0].center(plate)) - - def test_serial_dilution(self): - plate = load( - self.robot, - '96-flat', - '2', - 'plate' - ) - - tiprack = load( - self.robot, - 'tiprack-200ul', # container type from library - '1', # slot on deck - 'tiprack' # calibration reference for 1.2 compatibility - ) - - trough = load( - self.robot, - 'trough-12row', - '5', - 'trough' - ) - - trash = load( - self.robot, - 'point', - '3', - 'trash' - ) - - p200 = pipette.Pipette( - self.robot, - ul_per_mm=18.5, - trash_container=trash, - tip_racks=[tiprack], - min_volume=10, - max_volume=200, # These are variable - mount='left', - channels=1 - ) - p200.calibrate_plunger(top=0, bottom=10, blow_out=12, drop_tip=13) - - for t, col in enumerate(plate.cols): - p200.pick_up_tip(tiprack[t]) - - p200.aspirate(10, trough[t]) - p200.dispense(10, col[0]) - - for well, next_well in zip(col[:-1], col[1:]): - p200.aspirate(10, well) - p200.dispense(10, next_well) - p200.mix(repetitions=3, volume=10, location=next_well) - - p200.drop_tip(trash) - - # TODO: check for successful completion of the protocol +@pytest.fixture +def plate(robot): + return load(robot, '96-flat', '4') + + +def test_rows_cols(plate): + wells = [ + plate.rows[1]['2'], + plate.rows['B']['2'], + plate.rows['B'][1], + plate.rows[1][1], + plate.cols['2']['B'], + plate.cols[1]['B'], + plate.cols[1][1], + plate['B2'], + plate[9] + ] + + for well, next_well in zip(wells[:-1], wells[1:]): + assert well == next_well + + +def test_serial_dilution(robot): + plate = load( + robot, + '96-flat', + '2', + 'plate' + ) + + tiprack = load( + robot, + 'tiprack-200ul', # container type from library + '1', # slot on deck + 'tiprack' # calibration reference for 1.2 compatibility + ) + + trough = load( + robot, + 'trough-12row', + '5', + 'trough' + ) + + trash = load( + robot, + 'point', + '3', + 'trash' + ) + + p200 = pipette.Pipette( + robot, + ul_per_mm=18.5, + trash_container=trash, + tip_racks=[tiprack], + min_volume=10, + max_volume=200, # These are variable + mount='left', + channels=1 + ) + p200.calibrate_plunger(top=0, bottom=10, blow_out=12, drop_tip=13) + + for t, col in enumerate(plate.cols): + p200.pick_up_tip(tiprack[t]) + + p200.aspirate(10, trough[t]) + p200.dispense(10, col[0]) + + for well, next_well in zip(col[:-1], col[1:]): + p200.aspirate(10, well) + p200.dispense(10, next_well) + p200.mix(repetitions=3, volume=10, location=next_well) + + p200.drop_tip(trash) + + # TODO: check for successful completion of the protocol diff --git a/api/tests/opentrons/containers/test_placeable_unittest.py b/api/tests/opentrons/containers/test_placeable_unittest.py index 2bbe585b04c..a6b4b9d4456 100644 --- a/api/tests/opentrons/containers/test_placeable_unittest.py +++ b/api/tests/opentrons/containers/test_placeable_unittest.py @@ -1,6 +1,7 @@ -import unittest import math +import pytest + from opentrons.legacy_api.containers.placeable import ( Container, Well, @@ -13,265 +14,266 @@ # TODO: Modify all calls to get a Well to use the `wells`/`rows` methods -class PlaceableTestCase(unittest.TestCase): - def assertWellSeriesEqual(self, w1, w2): - if hasattr(w1, '__len__') and hasattr(w2, '__len__'): - if len(w1) != len(w2): +def assertWellSeriesEqual(w1, w2): + if hasattr(w1, '__len__') and hasattr(w2, '__len__'): + if len(w1) != len(w2): + print(w1) + print('lengths: {} and {}'.format(len(w1), len(w2))) + print(w2) + assert False + for i in range(len(w1)): + if w1[i] != w2[i]: print(w1) print('lengths: {} and {}'.format(len(w1), len(w2))) print(w2) assert False - for i in range(len(w1)): - if w1[i] != w2[i]: - print(w1) - print('lengths: {} and {}'.format(len(w1), len(w2))) - print(w2) - assert False - else: - self.assertEqual(w1, w2) - - def test_get_name(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - expected = '' - self.assertEqual(str(c['A1']), expected) - expected = '' - self.assertEqual(str(c), expected) - - def test_iterator(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - res = [well.coordinates() for well in c] - expected = [(0, 0, 0), (5, 0, 0), (0, 5, 0), (5, 5, 0)] - - self.assertListEqual(res, expected) - - def test_next(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - well = c['A1'] - expected = c.get_child_by_name('B1') - - self.assertEqual(next(well), expected) - - def test_cycle(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - cycle_iter = c.cycle() - for n in range(3): - for i in range(4): - self.assertEqual(next(cycle_iter), c[i]) - - def test_iter_method(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - cycle_iter = c.iter() + else: + assert w1 == w2 + + +def test_get_name(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + expected = '' + assert str(c['A1']) == expected + expected = '' + assert str(c) == expected + + +def test_iterator(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + res = [well.coordinates() for well in c] + expected = [(0, 0, 0), (5, 0, 0), (0, 5, 0), (5, 5, 0)] + + assert res == expected + + +def test_next(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + well = c['A1'] + expected = c.get_child_by_name('B1') + + assert next(well) == expected + + +def test_cycle(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + cycle_iter = c.cycle() + for n in range(3): for i in range(4): - self.assertEqual(next(cycle_iter), c[i]) - - def test_int_index(self): - c = generate_plate(4, 2, (5, 5), (0, 0), 5) - - self.assertEqual(c[3], c.get_child_by_name('B2')) - self.assertEqual(c[1], c.get_child_by_name('B1')) - - def test_add_placeables(self): - a = generate_plate(4, 2, (5, 5), (0, 0), 5) - b = generate_plate(4, 2, (5, 5), (0, 0), 5) - - result = a + b - self.assertEqual(len(result), 8) - for i in range(len(a)): - self.assertEqual(a[i], result[i]) - for i in range(len(b)): - self.assertEqual(b[i], result[i + len(a)]) - - result = a.cols(0) + b.rows(0) - self.assertEqual(len(result), 4) - self.assertEqual(a[0], result[0]) - self.assertEqual(a[1], result[1]) - self.assertEqual(b[0], result[2]) - self.assertEqual(b[2], result[3]) - - def test_named_well(self): - deck = Deck() - slot = Slot() - c = Container() - deck.add(slot, 'A1', (0, 0, 0)) - red = Well(properties={'radius': 5}) - blue = Well(properties={'radius': 5}) - c.add(red, "Red", (0, 0, 0)) - c.add(blue, "Blue", (10, 0, 0)) - slot.add(c) - - self.assertEqual(deck['A1'][0]['Red'], red) - - def test_generate_plate(self): - c = generate_plate( - wells=96, - cols=8, - spacing=(10, 15), - offset=(5, 15), - radius=5 - ) - - self.assertEqual(c['A1'].coordinates(), (5, 15, 0)) - self.assertEqual(c['B2'].coordinates(), (15, 30, 0)) - - def test_coordinates(self): - deck = Deck() - slot = Slot() - plate = generate_plate( - wells=96, - cols=8, - spacing=(10, 15), - offset=(5, 15), - radius=5 - ) - deck.add(slot, 'B2', (100, 200, 0)) - slot.add(plate) - - self.assertEqual(plate['A1'].coordinates(deck), (105, 215, 0)) - - def test_get_container_name(self): - deck = Deck() - slot = Slot() - c = Container() - deck.add(slot, 'A1', (0, 0, 0)) - red = Well(properties={'radius': 5}) - blue = Well(properties={'radius': 5}) - c.add(red, "Red", (0, 0, 0)) - c.add(blue, "Blue", (10, 0, 0)) - slot.add(c) - - self.assertEqual(red.get_name(), 'Red') - - def test_well_from_center(self): - deck = Deck() - slot = Slot() - plate = generate_plate( - wells=4, - cols=2, - spacing=(10, 10), - offset=(0, 0), - radius=5 - ) - deck.add(slot, 'A1', (0, 0, 0)) - slot.add(plate) - - self.assertEqual( - plate['B2'].center(), - (5, 5, 0)) - self.assertEqual( - plate['B2'].from_center(x=0.0, y=0.0, z=0.0), - (5, 5, 0)) - self.assertEqual( - plate['B2'].from_center(r=1.0, theta=math.pi / 2, h=0.0), - (5.0, 10.0, 0)) - - def test_get_all_children(self): - c1 = generate_plate(4, 2, (5, 5), (0, 0), 5) - c2 = generate_plate(4, 2, (5, 5), (0, 0), 5) - deck = Deck() - deck.add(c1, "A1", (0, 0, 0)) - deck.add(c2, "A2", (50, 50, 50)) - self.assertEqual(len(deck.get_all_children()), 10) - - def test_top_bottom(self): - deck = Deck() - slot = Slot() - plate = generate_plate( - wells=4, - cols=2, - spacing=(10, 10), - offset=(0, 0), - radius=5, - height=10 - ) - deck.add(slot, 'A1', (0, 0, 0)) - slot.add(plate) - - self.assertEqual( - plate['A1'].bottom(10), - (plate['A1'], Vector(5, 5, 10))) - self.assertEqual( - plate['A1'].top(10), - (plate['A1'], Vector(5, 5, 20))) - - self.assertEqual( - plate['A1'].bottom(10, radius=1.0, degrees=90), - (plate['A1'], Vector(5, 10, 10))) - self.assertEqual( - plate['A1'].top(10, radius=1.0, degrees=90), - (plate['A1'], Vector(5, 10, 20))) - - self.assertEqual( - plate['A1'].bottom(10, radius=0.5, degrees=270), - (plate['A1'], Vector(5, 2.5, 10.00))) - self.assertEqual( - plate['A1'].top(10, radius=0.5, degrees=270), - (plate['A1'], Vector(5, 2.5, 20.00))) - - def test_slice_with_strings(self): - c = generate_plate(96, 8, (9, 9), (16, 11), 2.5, 40) - self.assertWellSeriesEqual(c['A1':'A2'], c[0:8]) - self.assertWellSeriesEqual(c['A12':], c.cols[-1][0:]) - self.assertWellSeriesEqual(c.cols['4':'8'], c.cols[3:7]) - self.assertWellSeriesEqual(c.rows['B':'E'], c.rows[1:4]) - self.assertWellSeriesEqual(c.rows['B']['1':'7'], c.rows[1][0:6]) - - def test_wells(self): - c = generate_plate(96, 8, (9, 9), (16, 11), 2.5, 40) - - self.assertWellSeriesEqual(c.well(0), c[0]) - self.assertWellSeriesEqual(c.well('A2'), c['A2']) - self.assertWellSeriesEqual(c.wells(0), c[0]) - self.assertWellSeriesEqual(c.wells(), c[0:]) - - expected = [c[n] for n in ['A1', 'B2', 'C3']] - self.assertWellSeriesEqual(c.wells('A1', 'B2', 'C3'), expected) - self.assertWellSeriesEqual(c.get('A1', 'B2', 'C3'), expected) - self.assertWellSeriesEqual(c('A1', 'B2', 'C3'), expected) - - expected = [c.rows[0][0], c.rows[0][5]] - self.assertWellSeriesEqual(c.rows['A'].wells('1', '6'), expected) - self.assertWellSeriesEqual(c.rows['A'].get('1', '6'), expected) - - expected = [c.rows[0][0], c.rows[0][5]] - self.assertWellSeriesEqual(c.rows['A'].wells(['1', '6']), expected) - self.assertWellSeriesEqual(c.rows['A'].get('1', '6'), expected) - self.assertWellSeriesEqual(c.rows('A').get('1', '6'), expected) - - expected = c.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1') - self.assertWellSeriesEqual(c.wells('A1', to='H1'), expected) - self.assertWellSeriesEqual(c.get('A1', to='H1'), expected) - - expected = c.wells('A1', 'C1', 'E1', 'G1') - self.assertWellSeriesEqual(c.wells('A1', to='H1', step=2), expected) - self.assertWellSeriesEqual(c.get('A1', to='H1', step=2), expected) - - expected = c.cols['1':'12':2] - self.assertWellSeriesEqual(c.cols('1', to='12', step=2), expected) - - expected = c.wells( - 'A3', 'G2', 'E2', 'C2', 'A2', 'G1', 'E1', 'C1', 'A1') - self.assertWellSeriesEqual(c.wells('A3', to='A1', step=2), expected) - self.assertWellSeriesEqual(c.get('A3', to='A1', step=2), expected) - - expected = c.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1') - self.assertWellSeriesEqual(c.wells('A1', length=8), expected) - - expected = c.wells('A1', 'C1', 'E1', 'G1', 'A2', 'C2', 'E2', 'G2') - self.assertWellSeriesEqual(c.wells('A1', length=8, step=2), expected) - - expected = c.wells('A1', 'H12', 'G12', 'F12') - self.assertWellSeriesEqual(c.wells('A1', length=4, step=-1), expected) - - expected = c.wells('A1', 'H12', 'G12', 'F12') - self.assertWellSeriesEqual(c.wells('A1', length=-4, step=-1), expected) - - expected = c.wells('A1', 'H12', 'G12', 'F12') - self.assertWellSeriesEqual(c.wells('A1', length=-4, step=1), expected) - - expected = c.wells('A1', 'B1', 'C1', 'D1') - self.assertWellSeriesEqual(c.wells(length=4), expected) - - self.assertWellSeriesEqual(c.wells(43), c.wells(x=3, y=5)) - self.assertWellSeriesEqual(c.rows(3), c.wells(y=3)) - self.assertWellSeriesEqual(c.cols(4), c.wells(x=4)) - self.assertRaises(ValueError, c.wells, **{'x': '1', 'y': '2'}) + assert next(cycle_iter) == c[i] + + +def test_iter_method(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + cycle_iter = c.iter() + for i in range(4): + assert next(cycle_iter) == c[i] + + +def test_int_index(): + c = generate_plate(4, 2, (5, 5), (0, 0), 5) + + assert c[3] == c.get_child_by_name('B2') + assert c[1] == c.get_child_by_name('B1') + + +def test_add_placeables(): + a = generate_plate(4, 2, (5, 5), (0, 0), 5) + b = generate_plate(4, 2, (5, 5), (0, 0), 5) + + result = a + b + assert len(result) == 8 + for i in range(len(a)): + assert a[i] == result[i] + for i in range(len(b)): + assert b[i] == result[i + len(a)] + + result = a.cols(0) + b.rows(0) + assert len(result) == 4 + assert a[0] == result[0] + assert a[1] == result[1] + assert b[0] == result[2] + assert b[2] == result[3] + + +def test_named_well(): + deck = Deck() + slot = Slot() + c = Container() + deck.add(slot, 'A1', (0, 0, 0)) + red = Well(properties={'radius': 5}) + blue = Well(properties={'radius': 5}) + c.add(red, "Red", (0, 0, 0)) + c.add(blue, "Blue", (10, 0, 0)) + slot.add(c) + + assert deck['A1'][0]['Red'] == red + + +def test_generate_plate(): + c = generate_plate( + wells=96, + cols=8, + spacing=(10, 15), + offset=(5, 15), + radius=5 + ) + + assert c['A1'].coordinates() == (5, 15, 0) + assert c['B2'].coordinates() == (15, 30, 0) + + +def test_coordinates(): + deck = Deck() + slot = Slot() + plate = generate_plate( + wells=96, + cols=8, + spacing=(10, 15), + offset=(5, 15), + radius=5 + ) + deck.add(slot, 'B2', (100, 200, 0)) + slot.add(plate) + + assert plate['A1'].coordinates(deck) == (105, 215, 0) + + +def test_get_container_name(): + deck = Deck() + slot = Slot() + c = Container() + deck.add(slot, 'A1', (0, 0, 0)) + red = Well(properties={'radius': 5}) + blue = Well(properties={'radius': 5}) + c.add(red, "Red", (0, 0, 0)) + c.add(blue, "Blue", (10, 0, 0)) + slot.add(c) + + assert red.get_name() == 'Red' + + +def test_well_from_center(): + deck = Deck() + slot = Slot() + plate = generate_plate( + wells=4, + cols=2, + spacing=(10, 10), + offset=(0, 0), + radius=5 + ) + deck.add(slot, 'A1', (0, 0, 0)) + slot.add(plate) + + assert plate['B2'].center() == (5, 5, 0) + assert plate['B2'].from_center(x=0.0, y=0.0, z=0.0) == (5, 5, 0) + assert plate['B2'].from_center(r=1.0, theta=math.pi / 2, h=0.0)\ + == (5.0, 10.0, 0) + + +def test_get_all_children(): + c1 = generate_plate(4, 2, (5, 5), (0, 0), 5) + c2 = generate_plate(4, 2, (5, 5), (0, 0), 5) + deck = Deck() + deck.add(c1, "A1", (0, 0, 0)) + deck.add(c2, "A2", (50, 50, 50)) + assert len(deck.get_all_children()) == 10 + + +def test_top_bottom(): + deck = Deck() + slot = Slot() + plate = generate_plate( + wells=4, + cols=2, + spacing=(10, 10), + offset=(0, 0), + radius=5, + height=10 + ) + deck.add(slot, 'A1', (0, 0, 0)) + slot.add(plate) + + assert plate['A1'].bottom(10) == (plate['A1'], Vector(5, 5, 10)) + assert plate['A1'].top(10) == (plate['A1'], Vector(5, 5, 20)) + assert plate['A1'].bottom(10, radius=1.0, degrees=90) \ + == (plate['A1'], Vector(5, 10, 10)) + assert plate['A1'].top(10, radius=1.0, degrees=90)\ + == (plate['A1'], Vector(5, 10, 20)) + assert plate['A1'].bottom(10, radius=0.5, degrees=270)\ + == (plate['A1'], Vector(5, 2.5, 10.00)) + assert plate['A1'].top(10, radius=0.5, degrees=270)\ + == (plate['A1'], Vector(5, 2.5, 20.00)) + + +def test_slice_with_strings(): + c = generate_plate(96, 8, (9, 9), (16, 11), 2.5, 40) + assertWellSeriesEqual(c['A1':'A2'], c[0:8]) + assertWellSeriesEqual(c['A12':], c.cols[-1][0:]) + assertWellSeriesEqual(c.cols['4':'8'], c.cols[3:7]) + assertWellSeriesEqual(c.rows['B':'E'], c.rows[1:4]) + assertWellSeriesEqual(c.rows['B']['1':'7'], c.rows[1][0:6]) + + +def test_wells(): + c = generate_plate(96, 8, (9, 9), (16, 11), 2.5, 40) + + assertWellSeriesEqual(c.well(0), c[0]) + assertWellSeriesEqual(c.well('A2'), c['A2']) + assertWellSeriesEqual(c.wells(0), c[0]) + assertWellSeriesEqual(c.wells(), c[0:]) + + expected = [c[n] for n in ['A1', 'B2', 'C3']] + assertWellSeriesEqual(c.wells('A1', 'B2', 'C3'), expected) + assertWellSeriesEqual(c.get('A1', 'B2', 'C3'), expected) + assertWellSeriesEqual(c('A1', 'B2', 'C3'), expected) + + expected = [c.rows[0][0], c.rows[0][5]] + assertWellSeriesEqual(c.rows['A'].wells('1', '6'), expected) + assertWellSeriesEqual(c.rows['A'].get('1', '6'), expected) + + expected = [c.rows[0][0], c.rows[0][5]] + assertWellSeriesEqual(c.rows['A'].wells(['1', '6']), expected) + assertWellSeriesEqual(c.rows['A'].get('1', '6'), expected) + assertWellSeriesEqual(c.rows('A').get('1', '6'), expected) + + expected = c.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1') + assertWellSeriesEqual(c.wells('A1', to='H1'), expected) + assertWellSeriesEqual(c.get('A1', to='H1'), expected) + + expected = c.wells('A1', 'C1', 'E1', 'G1') + assertWellSeriesEqual(c.wells('A1', to='H1', step=2), expected) + assertWellSeriesEqual(c.get('A1', to='H1', step=2), expected) + + expected = c.cols['1':'12':2] + assertWellSeriesEqual(c.cols('1', to='12', step=2), expected) + + expected = c.wells( + 'A3', 'G2', 'E2', 'C2', 'A2', 'G1', 'E1', 'C1', 'A1') + assertWellSeriesEqual(c.wells('A3', to='A1', step=2), expected) + assertWellSeriesEqual(c.get('A3', to='A1', step=2), expected) + + expected = c.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1') + assertWellSeriesEqual(c.wells('A1', length=8), expected) + + expected = c.wells('A1', 'C1', 'E1', 'G1', 'A2', 'C2', 'E2', 'G2') + assertWellSeriesEqual(c.wells('A1', length=8, step=2), expected) + + expected = c.wells('A1', 'H12', 'G12', 'F12') + assertWellSeriesEqual(c.wells('A1', length=4, step=-1), expected) + + expected = c.wells('A1', 'H12', 'G12', 'F12') + assertWellSeriesEqual(c.wells('A1', length=-4, step=-1), expected) + + expected = c.wells('A1', 'H12', 'G12', 'F12') + assertWellSeriesEqual(c.wells('A1', length=-4, step=1), expected) + + expected = c.wells('A1', 'B1', 'C1', 'D1') + assertWellSeriesEqual(c.wells(length=4), expected) + + assertWellSeriesEqual(c.wells(43), c.wells(x=3, y=5)) + assertWellSeriesEqual(c.rows(3), c.wells(y=3)) + assertWellSeriesEqual(c.cols(4), c.wells(x=4)) + with pytest.raises(ValueError): + c.wells(**{'x': '1', 'y': '2'}) diff --git a/api/tests/opentrons/helpers/test_helpers.py b/api/tests/opentrons/helpers/test_helpers.py index 8b8addc58bf..e210c7790b8 100644 --- a/api/tests/opentrons/helpers/test_helpers.py +++ b/api/tests/opentrons/helpers/test_helpers.py @@ -1,36 +1,22 @@ -import unittest -from opentrons.legacy_api.robot import Robot from opentrons.helpers import helpers -from opentrons.legacy_api.instruments import pipette from opentrons.util.vector import Vector -from opentrons.legacy_api.containers import load as containers_load # TODO: Move `helpers` methods into either pipette or other non-generic place -class HelpersTest(unittest.TestCase): +def test_break_down_travel(): + # with 3-dimensional points + p1 = Vector(0, 0, 0) + p2 = Vector(10, -12, 14) + res = helpers.break_down_travel( + p1, p2, increment=5, mode='absolute') + assert res[-1] == p2 + assert len(res) == 5 - def setUp(self): - # TODO(Ahmed): Why does this test setup a plate, robot, container - # when it doesnt use them in any test cases? - self.robot = Robot() - self.p200 = pipette.Pipette( - self.robot, mount='left', max_volume=300, ul_per_mm=18.0) - self.plate = containers_load(self.robot, '96-flat', '3') - - def test_break_down_travel(self): - # with 3-dimensional points - p1 = Vector(0, 0, 0) - p2 = Vector(10, -12, 14) - res = helpers.break_down_travel( - p1, p2, increment=5, mode='absolute') - self.assertEqual(res[-1], p2) - self.assertEqual(len(res), 5) - - p1 = Vector(10, -12, 14) - res = helpers.break_down_travel(Vector(0, 0, 0), p1, mode='relative') - expected = Vector( - 0.46537410754407676, - -0.5584489290528921, - 0.6515237505617075) - self.assertEqual(res[-1], expected) - self.assertEqual(len(res), 5) + p1 = Vector(10, -12, 14) + res = helpers.break_down_travel(Vector(0, 0, 0), p1, mode='relative') + expected = Vector( + 0.46537410754407676, + -0.5584489290528921, + 0.6515237505617075) + assert res[-1] == expected + assert len(res) == 5 diff --git a/api/tests/opentrons/integration/test_protocol.py b/api/tests/opentrons/integration/test_protocol.py index 133bd4d7ec4..10226cdb07d 100644 --- a/api/tests/opentrons/integration/test_protocol.py +++ b/api/tests/opentrons/integration/test_protocol.py @@ -1,63 +1,55 @@ -import unittest - -from opentrons.legacy_api.robot import Robot from opentrons.legacy_api.containers import load as containers_load from opentrons.legacy_api.containers.placeable import Container, Deck from opentrons.legacy_api.instruments import pipette # TODO: Modify all calls to get a Well to use the `wells` method -class ProtocolTestCase(unittest.TestCase): - def setUp(self): - self.robot = Robot() - - def test_protocol_container_setup(self): - plate = containers_load(self.robot, '96-flat', '1', 'myPlate') - tiprack = containers_load(self.robot, 'tiprack-10ul', '5') - - containers_list = self.robot.get_containers() - self.assertEqual(len(containers_list), 3) +def test_protocol_container_setup(robot): + plate = containers_load(robot, '96-flat', '1', 'myPlate') + tiprack = containers_load(robot, 'tiprack-10ul', '5') - self.assertEqual(self.robot._deck['1']['myPlate'], plate) - self.assertEqual(self.robot._deck['5']['tiprack-10ul'], tiprack) + containers_list = robot.get_containers() + assert len(containers_list) == 3 + assert robot._deck['1']['myPlate'] == plate + assert robot._deck['5']['tiprack-10ul'] == tiprack - self.assertTrue(plate in containers_list) - self.assertTrue(tiprack in containers_list) + assert plate in containers_list + assert tiprack in containers_list - def test_protocol_head(self): - trash = containers_load(self.robot, 'point', '1', 'myTrash') - tiprack = containers_load(self.robot, 'tiprack-10ul', '5') - p200 = pipette.Pipette( - self.robot, - name='myPipette', - trash_container=trash, - tip_racks=[tiprack], - max_volume=200, - min_volume=10, # These are variable - ul_per_mm=18.0, - mount='left', - channels=1 - ) +def test_protocol_head(robot): + trash = containers_load(robot, 'point', '1', 'myTrash') + tiprack = containers_load(robot, 'tiprack-10ul', '5') - instruments_list = self.robot.get_instruments() - self.assertEqual(instruments_list[0], ('left', p200)) + p200 = pipette.Pipette( + robot, + name='myPipette', + trash_container=trash, + tip_racks=[tiprack], + max_volume=200, + min_volume=10, # These are variable + ul_per_mm=18.0, + mount='left', + channels=1 + ) + instruments_list = robot.get_instruments() + assert instruments_list[0] == ('left', p200) + instruments_list = robot.get_instruments('myPipette') + assert instruments_list[0] == ('left', p200) - instruments_list = self.robot.get_instruments('myPipette') - self.assertEqual(instruments_list[0], ('left', p200)) - def test_deck_setup(self): - deck = self.robot.deck +def test_deck_setup(robot): + deck = robot.deck - pip = pipette.Pipette( - self.robot, mount='left', max_volume=300, ul_per_mm=18.0) + pip = pipette.Pipette( + robot, mount='left', max_volume=300, ul_per_mm=18.0) - # Check that the fixed trash has loaded on to the pipette - trash = pip.trash_container - tiprack = containers_load(self.robot, 'tiprack-10ul', '5') + # Check that the fixed trash has loaded on to the pipette + trash = pip.trash_container + tiprack = containers_load(robot, 'tiprack-10ul', '5') - self.assertTrue(isinstance(tiprack, Container)) - self.assertTrue(isinstance(deck, Deck)) - # Check that well location is the same on the robot as the pipette - self.assertEqual(self.robot._deck['12']['tall-fixed-trash'][0], trash) - self.assertTrue(deck.has_container(tiprack)) + assert isinstance(tiprack, Container) + assert isinstance(deck, Deck) + # Check that well location is the same on the robot as the pipette + assert robot._deck['12']['tall-fixed-trash'][0] == trash + assert deck.has_container(tiprack) diff --git a/api/tests/opentrons/labware/test_pipette_unittest.py b/api/tests/opentrons/labware/test_pipette_unittest.py index 93edd7943e6..dd15128afe6 100644 --- a/api/tests/opentrons/labware/test_pipette_unittest.py +++ b/api/tests/opentrons/labware/test_pipette_unittest.py @@ -1,9 +1,10 @@ # pylama:ignore=E501 # TODO: Modify all calls to get a Well to use the `wells` method -import unittest from unittest import mock -from opentrons.legacy_api.robot import Robot + +import pytest + from opentrons.legacy_api.containers import load as containers_load from opentrons.legacy_api.instruments import Pipette from opentrons.legacy_api.containers.placeable import unpack_location @@ -12,1177 +13,1210 @@ from tests.opentrons import generate_plate -class PipetteTest(unittest.TestCase): - def setUp(self): - self.robot = Robot() - self.robot.home() - self.trash = containers_load(self.robot, 'point', '1') - self.tiprack1 = containers_load(self.robot, 'tiprack-10ul', '5') - self.tiprack2 = containers_load(self.robot, 'tiprack-10ul', '8') - - self.plate = containers_load(self.robot, '96-flat', '4') - - self.p200 = Pipette( - self.robot, - ul_per_mm=18.5, - trash_container=self.trash, - tip_racks=[self.tiprack1, self.tiprack2], - max_volume=200, - min_volume=10, # These are variable - mount='left', - channels=1, - name='other-pipette-for-transfer-tests' - ) - - self.p200.reset() - - self.p200.calibrate_plunger(top=0, bottom=10, blow_out=12, drop_tip=13) - self.robot.home() - - def tearDown(self): - del self.robot - - def test_bad_volume_percentage(self): - self.assertRaises(RuntimeError, self.p200._volume_percentage, -1) - - def test_add_instrument(self): - self.robot.reset() - Pipette(self.robot, ul_per_mm=18.5, max_volume=1000, mount='left') - self.assertRaises( - RuntimeError, - Pipette, - self.robot, - mount='left', - max_volume=100, - ul_per_mm=10) - - def test_aspirate_zero_volume(self): - assert self.robot.commands() == [] - self.p200.tip_attached = True - self.p200.aspirate(0) - assert self.robot.commands() == ['Aspirating 0 uL from ? at 1.0 speed'] # noqa - - def test_get_plunger_position(self): - - self.assertEqual(self.p200._get_plunger_position('top'), 0) - self.assertEqual(self.p200._get_plunger_position('bottom'), 10) - self.assertEqual(self.p200._get_plunger_position('blow_out'), 12) - self.assertEqual(self.p200._get_plunger_position('drop_tip'), 13) - - self.p200.plunger_positions['drop_tip'] = None - self.assertRaises( - RuntimeError, self.p200._get_plunger_position, 'drop_tip') - - self.assertRaises( - RuntimeError, self.p200._get_plunger_position, 'roll_out') - - def test_deprecated_axis_call(self): - import warnings - - warnings.filterwarnings('error') - # Check that user warning occurs when axis is called - self.assertRaises( - UserWarning, Pipette, self.robot, axis='a') - - # Check that the warning is still valid when max_volume is also used - self.assertRaises( - UserWarning, Pipette, self.robot, axis='a', max_volume=300) - - warnings.filterwarnings('default') - - def test_get_instruments_by_name(self): - self.p1000 = Pipette( - self.robot, - ul_per_mm=18.5, - trash_container=self.trash, - tip_racks=[self.tiprack1], - max_volume=1000, - min_volume=10, # These are variable - mount='right', - name='p1000', - channels=1, - aspirate_speed=300, - dispense_speed=500 - ) - result = list(self.robot.get_instruments('p1000')) - self.assertListEqual(result, [('right', self.p1000)]) - - def test_placeables_reference(self): - self.p200.tip_attached = True - self.p200.aspirate(100, self.plate[0]) - self.p200.dispense(100, self.plate[0]) - self.p200.aspirate(100, self.plate[20]) - self.p200.aspirate(100, self.plate[1]) - - expected = [ - self.plate[0], - self.plate[20], - self.plate[1] +@pytest.fixture +def local_test_pipette(robot): + trash = containers_load(robot, 'point', '1') + tiprack1 = containers_load(robot, 'tiprack-10ul', '5') + tiprack2 = containers_load(robot, 'tiprack-10ul', '8') + + plate = containers_load(robot, '96-flat', '4') + + p200 = Pipette( + robot, + ul_per_mm=18.5, + trash_container=trash, + tip_racks=[tiprack1, tiprack2], + max_volume=200, + min_volume=10, # These are variable + mount='left', + channels=1, + name='other-pipette-for-transfer-tests' + ) + + p200.reset() + + p200.calibrate_plunger(top=0, bottom=10, blow_out=12, drop_tip=13) + robot.home() + return trash, tiprack1, tiprack2, plate, p200 + + +def test_bad_volume_percentage(local_test_pipette): + _, _1, _2, _3, p200 = local_test_pipette + with pytest.raises(RuntimeError): + p200._volume_percentage(-1) + + +def test_add_instrument(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + robot.reset() + Pipette(robot, ul_per_mm=18.5, max_volume=1000, mount='left') + with pytest.raises(RuntimeError): + Pipette(robot, + mount='left', + max_volume=100, + ul_per_mm=10) + + +def test_aspirate_zero_volume(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + assert robot.commands() == [] + p200.tip_attached = True + p200.aspirate(0) + assert robot.commands() == ['Aspirating 0 uL from ? at 1.0 speed'] # noqa + + +def test_get_plunger_position(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + assert p200._get_plunger_position('top') == 0 + assert p200._get_plunger_position('bottom') == 10 + assert p200._get_plunger_position('blow_out') == 12 + assert p200._get_plunger_position('drop_tip') == 13 + + p200.plunger_positions['drop_tip'] = None + with pytest.raises(RuntimeError): + p200._get_plunger_position('drop_tip') + + with pytest.raises(RuntimeError): + p200._get_plunger_position('roll_out') + + +def test_deprecated_axis_call(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + import warnings + + warnings.filterwarnings('error') + # Check that user warning occurs when axis is called + with pytest.raises(UserWarning): + Pipette(robot, axis='a') + + # Check that the warning is still valid when max_volume is also used + with pytest.raises(UserWarning): + Pipette(robot, axis='a', max_volume=300) + + warnings.filterwarnings('default') + + +def test_get_instruments_by_name(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p1000 = Pipette( + robot, + ul_per_mm=18.5, + trash_container=trash, + tip_racks=[tiprack1], + max_volume=1000, + min_volume=10, # These are variable + mount='right', + name='p1000', + channels=1, + aspirate_speed=300, + dispense_speed=500 + ) + result = list(robot.get_instruments('p1000')) + assert result == [('right', p1000)] + + +def test_placeables_reference(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.tip_attached = True + p200.aspirate(100, plate[0]) + p200.dispense(100, plate[0]) + p200.aspirate(100, plate[20]) + p200.aspirate(100, plate[1]) + + expected = [ + plate[0], + plate[20], + plate[1] + ] + + assert p200.placeables == expected + + +def test_unpack_location(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + # TODO: remove when new labware system is promoted to production (it + # TODO: should not include the `unpack_location` magic + location = (plate[0], (1, 0, -1)) + res = unpack_location(location) + assert res == (plate[0], (1, 0, -1)) + + res = unpack_location(plate[0]) + assert res == (plate[0], plate[0].from_center(x=0, y=0, z=1)) + + +def test_aspirate_invalid_max_volume(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.tip_attached = True + with pytest.raises(RuntimeWarning): + p200.aspirate(500) + + +def test_volume_percentage(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + with pytest.raises(RuntimeError): + p200._volume_percentage(-1) + with pytest.raises(RuntimeError): + p200._volume_percentage(300) + assert p200._volume_percentage(100) == 0.5 + assert not robot.get_warnings() + p200._volume_percentage(p200.min_volume / 2) + assert len(robot.get_warnings()) == 1 + + +def test_add_tip(local_test_pipette, robot): + """ + This deals with z accrual behavior during tip add/remove, when +/- get + flipped in pose tracking logic + """ + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + prior_position = pose_tracker.absolute(robot.poses, p200) + p200._add_tip(42) + p200._remove_tip(42) + new_position = pose_tracker.absolute(robot.poses, p200) + + assert (new_position == prior_position).all() + + +def test_set_speed(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.set_speed(aspirate=100) + assert p200.speeds['aspirate'] == 100 + + p200.set_speed(dispense=100) + assert p200.speeds['dispense'] == 100 + + +def test_distribute(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + # Setting true instead of calling pick_up_tip because the test is + # currently based on an exact command list. Should make this better. + p200.distribute( + 30, + plate[0], + plate[1:9], + new_tip='always' + ) + + expected = [ + ['Distributing', '30', 'well A1', 'wells B1...A2'], + ['Transferring'], + ['Picking up tip'], + ['Aspirating', '190', 'well A1'], + ['Dispensing', '30', 'well B1'], + ['Dispensing', '30', 'well C1'], + ['Dispensing', '30', 'well D1'], + ['Dispensing', '30', 'well E1'], + ['Dispensing', '30', 'well F1'], + ['Dispensing', '30', 'well G1'], + ['Blow', 'well A1'], + ['Drop'], + ['Pick'], + ['Aspirating', '70', 'well A1'], + ['Dispensing', '30', 'well H1'], + ['Dispensing', '30', 'well A2'], + ['Blow', 'well A1'], + ['Drop'] + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + p200.reset() + p200.tip_attached = True + p200.distribute( + 30, + plate[0], + plate[1:9], + new_tip='never' + ) + + expected = [ + ['Distributing', '30', 'well A1', 'wells B1...A2'], + ['Transferring'], + ['Aspirating', '190', 'well A1'], + ['Dispensing', '30', 'well B1'], + ['Dispensing', '30', 'well C1'], + ['Dispensing', '30', 'well D1'], + ['Dispensing', '30', 'well E1'], + ['Dispensing', '30', 'well F1'], + ['Dispensing', '30', 'well G1'], + ['Blow', 'well A1'], + ['Aspirating', '70', 'well A1'], + ['Dispensing', '30', 'well H1'], + ['Dispensing', '30', 'well A2'], + ['Blow', 'well A1'] + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + p200.reset() + p200.distribute( + 30, + plate[0], + plate + ) + + total_dispenses = 0 + for c in robot.commands(): + if 'dispensing' in c.lower(): + total_dispenses += 1 + assert total_dispenses == 96 + robot.clear_commands() + + p200.reset() + p200.transfer( + 30, + plate[0], + plate[1:9], + trash=False + ) + + expected = [ + ['Transferring', '30', 'well A1'], + ['Pick'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well B1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well C1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well D1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well E1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well F1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well G1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well H1'], + ['Aspirating', '30', 'well A1'], + ['Dispensing', '30', 'well A2'], + ['Return'], + ['Drop'] + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + +def test_consolidate(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.consolidate( + 30, + plate[0:8], + plate['A2'], + new_tip='always' + ) + + expected = [ + ['Consolidating', '30'], + ['Transferring', '30'], + ['Pick'], + ['Aspirating', '30', 'Well A1'], + ['Aspirating', '30', 'Well B1'], + ['Aspirating', '30', 'Well C1'], + ['Aspirating', '30', 'Well D1'], + ['Aspirating', '30', 'Well E1'], + ['Aspirating', '30', 'Well F1'], + ['Dispensing', '180', 'Well A2'], + ['Drop'], + ['Pick'], + ['Aspirating', '30', 'Well G1'], + ['Aspirating', '30', 'Well H1'], + ['Dispensing', '60', 'Well A2'], + ['Drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + p200.reset() + p200.tip_attached = True + p200.consolidate( + 30, + plate[0:8], + plate['A2'], + new_tip='never' + ) + + expected = [ + ['Consolidating', '30'], + ['Transferring', '30'], + ['Aspirating', '30', 'Well A1'], + ['Aspirating', '30', 'Well B1'], + ['Aspirating', '30', 'Well C1'], + ['Aspirating', '30', 'Well D1'], + ['Aspirating', '30', 'Well E1'], + ['Aspirating', '30', 'Well F1'], + ['Dispensing', '180', 'Well A2'], + ['Aspirating', '30', 'Well G1'], + ['Aspirating', '30', 'Well H1'], + ['Dispensing', '60', 'Well A2'], + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + p200.reset() + p200.consolidate( + 30, + plate, + plate[0] + ) + + total_aspirates = 0 + for c in robot.commands(): + if 'aspirating' in c.lower(): + total_aspirates += 1 + assert total_aspirates == 96 + robot.clear_commands() + + p200.reset() + p200.transfer( + 30, + plate[0:8], + plate['A2'] + ) + + expected = [ + ['Transferring', '30'], + ['Pick'], + ['Aspirating', '30', 'Well A1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well B1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well C1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well D1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well E1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well F1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well G1'], + ['Dispensing', '30', 'Well A2'], + ['Aspirating', '30', 'Well H1'], + ['Dispensing', '30', 'Well A2'], + ['Drop'] + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + +def test_transfer(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.transfer( + 30, + plate[0:8], + plate[1:9], + new_tip='always', + air_gap=10, + disposal_vol=20, # ignored by transfer + touch_tip=True, + blow_out=True, + trash=True + ) + + expected = [ + ['Transferring', '30'], + ['pick'], + ['aspirating', '30', 'Well A1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well B1'], + ['dispensing', '30', 'Well B1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well B1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well C1'], + ['dispensing', '30', 'Well C1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well C1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well D1'], + ['dispensing', '30', 'Well D1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well D1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well E1'], + ['dispensing', '30', 'Well E1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well E1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well F1'], + ['dispensing', '30', 'Well F1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well F1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well G1'], + ['dispensing', '30', 'Well G1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well G1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well H1'], + ['dispensing', '30', 'Well H1'], + ['blow'], + ['touch'], + ['drop'], + ['pick'], + ['aspirating', '30', 'Well H1'], + ['air'], + ['aspirating', '10'], + ['touch'], + ['dispensing', '10', 'Well A2'], + ['dispensing', '30', 'Well A2'], + ['blow'], + ['touch'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + +def test_bad_transfer(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + + with pytest.raises(ValueError): + p200.transfer(30, plate[0:2], plate[0:3]) + + with pytest.raises(ValueError): + p200.transfer(30, plate[0:3], plate[0:2]) + + with pytest.raises(RuntimeError): + p200.transfer([30, 30, 30], plate[0:2], plate[0:2]) + + with pytest.raises(ValueError): + p200.transfer(30, plate[0], plate[1], new_tip='sometimes') + + +def test_divisible_locations(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.transfer( + 100, + plate[0:4], + plate[0:2] + ) + + expected = [ + ['transferring', '100'], + ['pick'], + ['aspirating', '100', 'Well A1'], + ['dispensing', '100', 'Well A1'], + ['aspirating', '100', 'Well B1'], + ['dispensing', '100', 'Well A1'], + ['aspirating', '100', 'Well C1'], + ['dispensing', '100', 'Well B1'], + ['aspirating', '100', 'Well D1'], + ['dispensing', '100', 'Well B1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + p200.reset() + p200.consolidate( + 100, + plate[0:4], + plate[0:2] + ) + expected = [ + ['consolidating', '100'], + ['transferring', '100'], + ['pick'], + ['aspirating', '100', 'Well A1'], + ['aspirating', '100', 'Well B1'], + ['dispensing', '200', 'Well A1'], + ['aspirating', '100', 'Well C1'], + ['aspirating', '100', 'Well D1'], + ['dispensing', '200', 'Well B1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + p200.reset() + p200.distribute( + 100, + plate[0:2], + plate[0:4], + disposal_vol=0 + ) + + expected = [ + ['distributing', '100'], + ['transferring', '100'], + ['pick'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '100', 'Well A1'], + ['dispensing', '100', 'Well B1'], + ['aspirating', '200', 'Well B1'], + ['dispensing', '100', 'Well C1'], + ['dispensing', '100', 'Well D1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_transfer_mix(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.transfer( + 200, + plate[0], + plate[1], + mix_before=(1, 10), + mix_after=(1, 10) + ) + + expected = [ + ['Transferring', '200'], + ['pick'], + ['mix', '10'], + ['aspirating', 'Well A1'], + ['dispensing'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '200', 'Well B1'], + ['mix', '10'], + ['aspirating', 'Well B1'], + ['dispensing'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + +def test_transfer_air_gap(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.transfer( + 120, + plate[0], + plate[1], + air_gap=20 + ) + expected = [ + ['Transferring', '120'], + ['pick'], + ['aspirating', '120', 'Well A1'], + ['air gap'], + ['aspirating', '20'], + ['dispensing', '20', 'Well B1'], + ['dispensing', '120', 'Well B1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + +def test_consolidate_air_gap(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.consolidate( + 60, + plate[0:2], + plate[2], + air_gap=20 + ) + expected = [ + ['consolidating', '60'], + ['transferring', '60'], + ['pick'], + ['aspirating', '60', 'Well A1'], + ['aspirating', '60', 'Well B1'], + ['dispensing', '120', 'Well C1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected) + robot.clear_commands() + + +def test_distribute_air_gap(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.distribute( + 60, + plate[2], + plate[0:2], + air_gap=20 + ) + expected = [ + ['distributing', '60'], + ['transferring', '60'], + ['pick'], + ['aspirating', '130', 'Well C1'], + ['air gap'], + ['aspirating', '20'], + ['dispensing', '20'], + ['dispensing', '60', 'Well A1'], + ['air gap'], + ['aspirating', '20'], + ['dispensing', '20'], + ['dispensing', '60', 'Well B1'], + ['blow', 'Well A1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), expected=expected) + robot.clear_commands() + + +def test_distribute_air_gap_and_disposal_vol(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.distribute( + 60, + plate[2], + plate[0:2], + air_gap=20, + disposal_vol=20 + ) + expected = [ + ['distributing', '60'], + ['transferring', '60'], + ['pick'], + ['aspirating', '140', 'Well C1'], + ['air gap'], + ['aspirating', '20'], + ['dispensing', '20', 'Well A1'], + ['dispensing', '60', 'Well A1'], + ['air gap'], + ['aspirating', '20'], + ['dispensing', '20', 'Well B1'], + ['dispensing', '60', 'Well B1'], + ['blow', 'Well A1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_consolidate_mix(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.consolidate( + 200, + plate[0:2], + plate[2], + mix_before=(1, 10), + mix_after=(1, 10) + ) + expected = [ + ['consolidating', '200'], + ['transferring', '200'], + ['pick'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '200', 'Well C1'], + ['mix', '10'], + ['aspirating', 'Well C1'], + ['dispensing'], + ['aspirating', '200', 'Well B1'], + ['dispensing', '200', 'Well C1'], + ['mix', '10'], + ['aspirating', 'Well C1'], + ['dispensing'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_distribute_mix(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.distribute( + 200, + plate[0], + plate[1:3], + mix_before=(1, 10), + mix_after=(1, 10) + ) + expected = [ + ['distributing', '200'], + ['transferring', '200'], + ['pick'], + ['mix', '10'], + ['aspirating', 'Well A1'], + ['dispensing'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '200', 'Well B1'], + ['mix', '10'], + ['aspirating', 'Well A1'], + ['dispensing'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '200', 'Well C1'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_transfer_multichannel(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.channels = 8 + p200.transfer( + 200, + plate.cols[0], + plate.cols[1], + touch_tip=False, + blow_out=False, + trash=False + ) + expected = [ + ['Transferring', '200'], + ['pick'], + ['aspirating', '200', 'wells A1...H1'], + ['dispensing', '200', 'wells A2...H2'], + ['return'], + ['drop'] + ] + fuzzy_assert(robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_transfer_single_channel(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.reset() + p200.channels = 1 + p200.transfer( + 200, + plate.cols('1', '2'), + plate.cols('3'), + touch_tip=False, + blow_out=False, + trash=False + ) + + expected = [ + ['Transferring', '200'], + ['pick'], + ['aspirating', '200', 'Well A1'], + ['dispensing', '200', 'Well A3'], + ['aspirating', '200', 'Well B1'], + ['dispensing', '200', 'Well A3'], + ['aspirating', '200', 'Well C1'], + ['dispensing', '200', 'Well B3'], + ['aspirating', '200', 'Well D1'], + ['dispensing', '200', 'Well B3'], + ['aspirating', '200', 'Well E1'], + ['dispensing', '200', 'Well C3'], + ['aspirating', '200', 'Well F1'], + ['dispensing', '200', 'Well C3'], + ['aspirating', '200', 'Well G1'], + ['dispensing', '200', 'Well D3'], + ['aspirating', '200', 'Well H1'], + ['dispensing', '200', 'Well D3'], + ['aspirating', '200', 'Well A2'], + ['dispensing', '200', 'Well E3'], + ['aspirating', '200', 'Well B2'], + ['dispensing', '200', 'Well E3'], + ['aspirating', '200', 'Well C2'], + ['dispensing', '200', 'Well F3'], + ['aspirating', '200', 'Well D2'], + ['dispensing', '200', 'Well F3'], + ['aspirating', '200', 'Well E2'], + ['dispensing', '200', 'Well G3'], + ['aspirating', '200', 'Well F2'], + ['dispensing', '200', 'Well G3'], + ['aspirating', '200', 'Well G2'], + ['dispensing', '200', 'Well H3'], + ['aspirating', '200', 'Well H2'], + ['dispensing', '200', 'Well H3'], + ['return'], + ['drop'] + ] + + fuzzy_assert( + robot.commands(), + expected=expected + ) + robot.clear_commands() + + +def test_touch_tip(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.pick_up_tip() + p200.robot.move_to = mock.Mock() + p200.touch_tip(plate[0]) + p200.touch_tip(v_offset=-3) + p200.touch_tip(plate[1], radius=0.5) + + expected = [ + mock.call(plate[0], + instrument=p200, + strategy='arc'), + + mock.call( + (plate[0], (6.40, 3.20, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (0.00, 3.20, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (3.20, 6.40, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (3.20, 0.00, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (6.40, 3.20, 7.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (0.00, 3.20, 7.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (3.20, 6.40, 7.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[0], (3.20, 0.00, 7.50)), + instrument=p200, + strategy='direct'), + mock.call(plate[1], + instrument=p200, + strategy='arc'), + mock.call( + (plate[1], (4.80, 3.20, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[1], (1.60, 3.20, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[1], (3.20, 4.80, 9.50)), + instrument=p200, + strategy='direct'), + mock.call( + (plate[1], (3.20, 1.60, 9.50)), + instrument=p200, + strategy='direct') + ] + + assert expected == p200.robot.move_to.mock_calls + + +def test_mix(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + # It is necessary to aspirate before it is mocked out + # so that you have liquid + p200.pick_up_tip() + p200.aspirate = mock.Mock() + p200.dispense = mock.Mock() + p200.mix(3, 100, plate[1]) + + dispense_expected = [ + mock.call.dispense(100, rate=1.0), + mock.call.dispense(100, rate=1.0), + mock.call.dispense(100, rate=1.0) + ] + assert p200.dispense.mock_calls == dispense_expected + + aspirate_expected = [ + mock.call.aspirate(volume=100, location=plate[1], rate=1.0), + mock.call.aspirate(100, rate=1.0), + mock.call.aspirate(100, rate=1.0) + ] + assert p200.aspirate.mock_calls == aspirate_expected + + +def test_air_gap(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.pick_up_tip() + p200.aspirate(50, plate[0]) + p200.air_gap() + assert p200.current_volume == 200 + + p200.dispense() + p200.aspirate(50, plate[1]) + p200.air_gap(10) + assert p200.current_volume == 60 + + p200.dispense() + p200.aspirate(50, plate[2]) + p200.air_gap(10, 10) + assert p200.current_volume == 60 + + p200.dispense() + p200.aspirate(50, plate[2]) + p200.air_gap(0) + assert p200.current_volume == 50 + + +def test_pipette_home(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.home() + assert len(robot.commands()) == 1 + + +def test_mix_with_named_args(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.current_volume = 100 + p200.pick_up_tip() + p200.aspirate = mock.Mock() + p200.dispense = mock.Mock() + p200.mix(volume=50, repetitions=2) + + assert \ + p200.dispense.mock_calls == \ + [ + mock.call.dispense(50, rate=1.0), + mock.call.dispense(50, rate=1.0) ] - self.assertEqual(self.p200.placeables, expected) - - def test_unpack_location(self): - # TODO: remove when new labware system is promoted to production (it - # TODO: should not include the `unpack_location` magic - location = (self.plate[0], (1, 0, -1)) - res = unpack_location(location) - self.assertEqual(res, (self.plate[0], (1, 0, -1))) - - res = unpack_location(self.plate[0]) - self.assertEqual( - res, - (self.plate[0], self.plate[0].from_center(x=0, y=0, z=1))) - - def test_aspirate_invalid_max_volume(self): - self.p200.tip_attached = True - with self.assertRaises(RuntimeWarning): - self.p200.aspirate(500) - - def test_volume_percentage(self): - self.assertRaises(RuntimeError, self.p200._volume_percentage, -1) - self.assertRaises(RuntimeError, self.p200._volume_percentage, 300) - self.assertEqual(self.p200._volume_percentage(100), 0.5) - self.assertEqual(len(self.robot.get_warnings()), 0) - self.p200._volume_percentage(self.p200.min_volume / 2) - self.assertEqual(len(self.robot.get_warnings()), 1) - - def test_add_tip(self): - """ - This deals with z accrual behavior during tip add/remove, when +/- get - flipped in pose tracking logic - """ - prior_position = pose_tracker.absolute(self.robot.poses, self.p200) - self.p200._add_tip(42) - self.p200._remove_tip(42) - new_position = pose_tracker.absolute(self.robot.poses, self.p200) - - assert (new_position == prior_position).all() - - def test_set_speed(self): - self.p200.set_speed(aspirate=100) - self.assertEqual(self.p200.speeds['aspirate'], 100) - - self.p200.set_speed(dispense=100) - self.assertEqual(self.p200.speeds['dispense'], 100) - - def test_distribute(self): - self.p200.reset() - # Setting true instead of calling pick_up_tip because the test is - # currently based on an exact command list. Should make this better. - self.p200.distribute( - 30, - self.plate[0], - self.plate[1:9], - new_tip='always' - ) - - expected = [ - ['Distributing', '30', 'well A1', 'wells B1...A2'], - ['Transferring'], - ['Picking up tip'], - ['Aspirating', '190', 'well A1'], - ['Dispensing', '30', 'well B1'], - ['Dispensing', '30', 'well C1'], - ['Dispensing', '30', 'well D1'], - ['Dispensing', '30', 'well E1'], - ['Dispensing', '30', 'well F1'], - ['Dispensing', '30', 'well G1'], - ['Blow', 'well A1'], - ['Drop'], - ['Pick'], - ['Aspirating', '70', 'well A1'], - ['Dispensing', '30', 'well H1'], - ['Dispensing', '30', 'well A2'], - ['Blow', 'well A1'], - ['Drop'] - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - self.p200.reset() - self.p200.tip_attached = True - self.p200.distribute( - 30, - self.plate[0], - self.plate[1:9], - new_tip='never' - ) - - expected = [ - ['Distributing', '30', 'well A1', 'wells B1...A2'], - ['Transferring'], - ['Aspirating', '190', 'well A1'], - ['Dispensing', '30', 'well B1'], - ['Dispensing', '30', 'well C1'], - ['Dispensing', '30', 'well D1'], - ['Dispensing', '30', 'well E1'], - ['Dispensing', '30', 'well F1'], - ['Dispensing', '30', 'well G1'], - ['Blow', 'well A1'], - ['Aspirating', '70', 'well A1'], - ['Dispensing', '30', 'well H1'], - ['Dispensing', '30', 'well A2'], - ['Blow', 'well A1'] - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - self.p200.reset() - self.p200.distribute( - 30, - self.plate[0], - self.plate - ) - - total_dispenses = 0 - for c in self.robot.commands(): - if 'dispensing' in c.lower(): - total_dispenses += 1 - self.assertEqual(total_dispenses, 96) - self.robot.clear_commands() - - self.p200.reset() - self.p200.transfer( - 30, - self.plate[0], - self.plate[1:9], - trash=False - ) - - expected = [ - ['Transferring', '30', 'well A1'], - ['Pick'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well B1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well C1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well D1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well E1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well F1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well G1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well H1'], - ['Aspirating', '30', 'well A1'], - ['Dispensing', '30', 'well A2'], - ['Return'], - ['Drop'] - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - def test_consolidate(self): - - self.p200.reset() - self.p200.consolidate( - 30, - self.plate[0:8], - self.plate['A2'], - new_tip='always' - ) - - expected = [ - ['Consolidating', '30'], - ['Transferring', '30'], - ['Pick'], - ['Aspirating', '30', 'Well A1'], - ['Aspirating', '30', 'Well B1'], - ['Aspirating', '30', 'Well C1'], - ['Aspirating', '30', 'Well D1'], - ['Aspirating', '30', 'Well E1'], - ['Aspirating', '30', 'Well F1'], - ['Dispensing', '180', 'Well A2'], - ['Drop'], - ['Pick'], - ['Aspirating', '30', 'Well G1'], - ['Aspirating', '30', 'Well H1'], - ['Dispensing', '60', 'Well A2'], - ['Drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - self.p200.reset() - self.p200.tip_attached = True - self.p200.consolidate( - 30, - self.plate[0:8], - self.plate['A2'], - new_tip='never' - ) - - expected = [ - ['Consolidating', '30'], - ['Transferring', '30'], - ['Aspirating', '30', 'Well A1'], - ['Aspirating', '30', 'Well B1'], - ['Aspirating', '30', 'Well C1'], - ['Aspirating', '30', 'Well D1'], - ['Aspirating', '30', 'Well E1'], - ['Aspirating', '30', 'Well F1'], - ['Dispensing', '180', 'Well A2'], - ['Aspirating', '30', 'Well G1'], - ['Aspirating', '30', 'Well H1'], - ['Dispensing', '60', 'Well A2'], - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - self.p200.reset() - self.p200.consolidate( - 30, - self.plate, - self.plate[0] - ) - - total_aspirates = 0 - for c in self.robot.commands(): - if 'aspirating' in c.lower(): - total_aspirates += 1 - self.assertEqual(total_aspirates, 96) - self.robot.clear_commands() - - self.p200.reset() - self.p200.transfer( - 30, - self.plate[0:8], - self.plate['A2'] - ) - - expected = [ - ['Transferring', '30'], - ['Pick'], - ['Aspirating', '30', 'Well A1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well B1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well C1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well D1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well E1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well F1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well G1'], - ['Dispensing', '30', 'Well A2'], - ['Aspirating', '30', 'Well H1'], - ['Dispensing', '30', 'Well A2'], - ['Drop'] - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - def test_transfer(self): - - self.p200.reset() - self.p200.transfer( - 30, - self.plate[0:8], - self.plate[1:9], - new_tip='always', - air_gap=10, - disposal_vol=20, # ignored by transfer - touch_tip=True, - blow_out=True, - trash=True - ) - - expected = [ - ['Transferring', '30'], - ['pick'], - ['aspirating', '30', 'Well A1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well B1'], - ['dispensing', '30', 'Well B1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well B1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well C1'], - ['dispensing', '30', 'Well C1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well C1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well D1'], - ['dispensing', '30', 'Well D1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well D1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well E1'], - ['dispensing', '30', 'Well E1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well E1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well F1'], - ['dispensing', '30', 'Well F1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well F1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well G1'], - ['dispensing', '30', 'Well G1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well G1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well H1'], - ['dispensing', '30', 'Well H1'], - ['blow'], - ['touch'], - ['drop'], - ['pick'], - ['aspirating', '30', 'Well H1'], - ['air'], - ['aspirating', '10'], - ['touch'], - ['dispensing', '10', 'Well A2'], - ['dispensing', '30', 'Well A2'], - ['blow'], - ['touch'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - def test_bad_transfer(self): - self.p200.reset() - - self.assertRaises( - ValueError, - self.p200.transfer, - 30, - self.plate[0:2], - self.plate[0:3] - ) - - self.assertRaises( - ValueError, - self.p200.transfer, - 30, - self.plate[0:3], - self.plate[0:2] - ) - - self.assertRaises( - RuntimeError, - self.p200.transfer, - [30, 30, 30], - self.plate[0:2], - self.plate[0:2] - ) - - self.assertRaises( - ValueError, - self.p200.transfer, - 30, - self.plate[0], - self.plate[1], - new_tip='sometimes' - ) - - self.assertRaises( - ValueError, - self.p200.transfer, - [20, 20, 20, 20], - self.plate[0:3], - self.plate[1:4], - new_tip='sometimes' - ) - - def test_divisible_locations(self): - self.p200.reset() - self.p200.transfer( - 100, - self.plate[0:4], - self.plate[0:2] - ) - - expected = [ - ['transferring', '100'], - ['pick'], - ['aspirating', '100', 'Well A1'], - ['dispensing', '100', 'Well A1'], - ['aspirating', '100', 'Well B1'], - ['dispensing', '100', 'Well A1'], - ['aspirating', '100', 'Well C1'], - ['dispensing', '100', 'Well B1'], - ['aspirating', '100', 'Well D1'], - ['dispensing', '100', 'Well B1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - self.p200.reset() - self.p200.consolidate( - 100, - self.plate[0:4], - self.plate[0:2] - ) - expected = [ - ['consolidating', '100'], - ['transferring', '100'], - ['pick'], - ['aspirating', '100', 'Well A1'], - ['aspirating', '100', 'Well B1'], - ['dispensing', '200', 'Well A1'], - ['aspirating', '100', 'Well C1'], - ['aspirating', '100', 'Well D1'], - ['dispensing', '200', 'Well B1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - self.p200.reset() - self.p200.distribute( - 100, - self.plate[0:2], - self.plate[0:4], - disposal_vol=0 - ) - - expected = [ - ['distributing', '100'], - ['transferring', '100'], - ['pick'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '100', 'Well A1'], - ['dispensing', '100', 'Well B1'], - ['aspirating', '200', 'Well B1'], - ['dispensing', '100', 'Well C1'], - ['dispensing', '100', 'Well D1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_transfer_mix(self): - self.p200.reset() - self.p200.transfer( - 200, - self.plate[0], - self.plate[1], - mix_before=(1, 10), - mix_after=(1, 10) - ) - - expected = [ - ['Transferring', '200'], - ['pick'], - ['mix', '10'], - ['aspirating', 'Well A1'], - ['dispensing'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '200', 'Well B1'], - ['mix', '10'], - ['aspirating', 'Well B1'], - ['dispensing'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - def test_transfer_air_gap(self): - self.p200.reset() - self.p200.transfer( - 120, - self.plate[0], - self.plate[1], - air_gap=20 - ) - expected = [ - ['Transferring', '120'], - ['pick'], - ['aspirating', '120', 'Well A1'], - ['air gap'], - ['aspirating', '20'], - ['dispensing', '20', 'Well B1'], - ['dispensing', '120', 'Well B1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - def test_consolidate_air_gap(self): - self.p200.reset() - self.p200.consolidate( - 60, - self.plate[0:2], - self.plate[2], - air_gap=20 - ) - expected = [ - ['consolidating', '60'], - ['transferring', '60'], - ['pick'], - ['aspirating', '60', 'Well A1'], - ['aspirating', '60', 'Well B1'], - ['dispensing', '120', 'Well C1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected) - self.robot.clear_commands() - - def test_distribute_air_gap(self): - self.p200.reset() - self.p200.distribute( - 60, - self.plate[2], - self.plate[0:2], - air_gap=20 - ) - expected = [ - ['distributing', '60'], - ['transferring', '60'], - ['pick'], - ['aspirating', '130', 'Well C1'], - ['air gap'], - ['aspirating', '20'], - ['dispensing', '20'], - ['dispensing', '60', 'Well A1'], - ['air gap'], - ['aspirating', '20'], - ['dispensing', '20'], - ['dispensing', '60', 'Well B1'], - ['blow', 'Well A1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), expected=expected) - self.robot.clear_commands() - - def test_distribute_air_gap_and_disposal_vol(self): - self.p200.reset() - self.p200.distribute( - 60, - self.plate[2], - self.plate[0:2], - air_gap=20, - disposal_vol=20 - ) - expected = [ - ['distributing', '60'], - ['transferring', '60'], - ['pick'], - ['aspirating', '140', 'Well C1'], - ['air gap'], - ['aspirating', '20'], - ['dispensing', '20', 'Well A1'], - ['dispensing', '60', 'Well A1'], - ['air gap'], - ['aspirating', '20'], - ['dispensing', '20', 'Well B1'], - ['dispensing', '60', 'Well B1'], - ['blow', 'Well A1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_consolidate_mix(self): - self.p200.reset() - self.p200.consolidate( - 200, - self.plate[0:2], - self.plate[2], - mix_before=(1, 10), - mix_after=(1, 10) - ) - expected = [ - ['consolidating', '200'], - ['transferring', '200'], - ['pick'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '200', 'Well C1'], - ['mix', '10'], - ['aspirating', 'Well C1'], - ['dispensing'], - ['aspirating', '200', 'Well B1'], - ['dispensing', '200', 'Well C1'], - ['mix', '10'], - ['aspirating', 'Well C1'], - ['dispensing'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_distribute_mix(self): - self.p200.reset() - self.p200.distribute( - 200, - self.plate[0], - self.plate[1:3], - mix_before=(1, 10), - mix_after=(1, 10) - ) - expected = [ - ['distributing', '200'], - ['transferring', '200'], - ['pick'], - ['mix', '10'], - ['aspirating', 'Well A1'], - ['dispensing'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '200', 'Well B1'], - ['mix', '10'], - ['aspirating', 'Well A1'], - ['dispensing'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '200', 'Well C1'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_transfer_multichannel(self): - self.p200.reset() - self.p200.channels = 8 - self.p200.transfer( - 200, - self.plate.cols[0], - self.plate.cols[1], - touch_tip=False, - blow_out=False, - trash=False - ) - expected = [ - ['Transferring', '200'], - ['pick'], - ['aspirating', '200', 'wells A1...H1'], - ['dispensing', '200', 'wells A2...H2'], - ['return'], - ['drop'] - ] - fuzzy_assert(self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_transfer_single_channel(self): - self.p200.reset() - self.p200.channels = 1 - self.p200.transfer( - 200, - self.plate.cols('1', '2'), - self.plate.cols('3'), - touch_tip=False, - blow_out=False, - trash=False - ) - - expected = [ - ['Transferring', '200'], - ['pick'], - ['aspirating', '200', 'Well A1'], - ['dispensing', '200', 'Well A3'], - ['aspirating', '200', 'Well B1'], - ['dispensing', '200', 'Well A3'], - ['aspirating', '200', 'Well C1'], - ['dispensing', '200', 'Well B3'], - ['aspirating', '200', 'Well D1'], - ['dispensing', '200', 'Well B3'], - ['aspirating', '200', 'Well E1'], - ['dispensing', '200', 'Well C3'], - ['aspirating', '200', 'Well F1'], - ['dispensing', '200', 'Well C3'], - ['aspirating', '200', 'Well G1'], - ['dispensing', '200', 'Well D3'], - ['aspirating', '200', 'Well H1'], - ['dispensing', '200', 'Well D3'], - ['aspirating', '200', 'Well A2'], - ['dispensing', '200', 'Well E3'], - ['aspirating', '200', 'Well B2'], - ['dispensing', '200', 'Well E3'], - ['aspirating', '200', 'Well C2'], - ['dispensing', '200', 'Well F3'], - ['aspirating', '200', 'Well D2'], - ['dispensing', '200', 'Well F3'], - ['aspirating', '200', 'Well E2'], - ['dispensing', '200', 'Well G3'], - ['aspirating', '200', 'Well F2'], - ['dispensing', '200', 'Well G3'], - ['aspirating', '200', 'Well G2'], - ['dispensing', '200', 'Well H3'], - ['aspirating', '200', 'Well H2'], - ['dispensing', '200', 'Well H3'], - ['return'], - ['drop'] + assert \ + p200.aspirate.mock_calls == \ + [ + mock.call.aspirate(volume=50, + location=None, + rate=1.0), + mock.call.aspirate(50, rate=1.0) ] - fuzzy_assert( - self.robot.commands(), - expected=expected - ) - self.robot.clear_commands() - - def test_touch_tip(self): - self.p200.pick_up_tip() - self.p200.robot.move_to = mock.Mock() - self.p200.touch_tip(self.plate[0]) - self.p200.touch_tip(v_offset=-3) - self.p200.touch_tip(self.plate[1], radius=0.5) - - expected = [ - mock.call(self.plate[0], - instrument=self.p200, - strategy='arc'), - - mock.call( - (self.plate[0], (6.40, 3.20, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (0.00, 3.20, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (3.20, 6.40, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (3.20, 0.00, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (6.40, 3.20, 7.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (0.00, 3.20, 7.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (3.20, 6.40, 7.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[0], (3.20, 0.00, 7.50)), - instrument=self.p200, - strategy='direct'), - mock.call(self.plate[1], - instrument=self.p200, - strategy='arc'), - mock.call( - (self.plate[1], (4.80, 3.20, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[1], (1.60, 3.20, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[1], (3.20, 4.80, 9.50)), - instrument=self.p200, - strategy='direct'), - mock.call( - (self.plate[1], (3.20, 1.60, 9.50)), - instrument=self.p200, - strategy='direct') - ] - self.assertEqual(expected, self.p200.robot.move_to.mock_calls) +def test_tip_tracking_simple(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.move_to = mock.Mock() + p200.pick_up_tip() + p200.tip_attached = False # prior expectation, for test only + p200.pick_up_tip() - def test_mix(self): - # It is necessary to aspirate before it is mocked out - # so that you have liquid - self.p200.pick_up_tip() - self.p200.aspirate = mock.Mock() - self.p200.dispense = mock.Mock() - self.p200.mix(3, 100, self.plate[1]) + assert p200.move_to.mock_calls == \ + build_pick_up_tip(p200, tiprack1[0]) + \ + build_pick_up_tip(p200, tiprack1[1]) - dispense_expected = [ - mock.call.dispense(100, rate=1.0), - mock.call.dispense(100, rate=1.0), - mock.call.dispense(100, rate=1.0) - ] - self.assertEqual(self.p200.dispense.mock_calls, dispense_expected) - aspirate_expected = [ - mock.call.aspirate(volume=100, location=self.plate[1], rate=1.0), - mock.call.aspirate(100, rate=1.0), - mock.call.aspirate(100, rate=1.0) - ] - self.assertEqual(self.p200.aspirate.mock_calls, aspirate_expected) - - def test_air_gap(self): - self.p200.pick_up_tip() - self.p200.aspirate(50, self.plate[0]) - self.p200.air_gap() - self.assertEqual(self.p200.current_volume, 200) - - self.p200.dispense() - self.p200.aspirate(50, self.plate[1]) - self.p200.air_gap(10) - self.assertEqual(self.p200.current_volume, 60) - - self.p200.dispense() - self.p200.aspirate(50, self.plate[2]) - self.p200.air_gap(10, 10) - self.assertEqual(self.p200.current_volume, 60) - - self.p200.dispense() - self.p200.aspirate(50, self.plate[2]) - self.p200.air_gap(0) - self.assertEqual(self.p200.current_volume, 50) - - def test_pipette_home(self): - self.p200.home() - self.assertEqual(len(self.robot.commands()), 1) - - def test_mix_with_named_args(self): - self.p200.current_volume = 100 - self.p200.pick_up_tip() - self.p200.aspirate = mock.Mock() - self.p200.dispense = mock.Mock() - self.p200.mix(volume=50, repetitions=2) - - self.assertEqual( - self.p200.dispense.mock_calls, - [ - mock.call.dispense(50, rate=1.0), - mock.call.dispense(50, rate=1.0) - ] - ) - self.assertEqual( - self.p200.aspirate.mock_calls, - [ - mock.call.aspirate(volume=50, - location=None, - rate=1.0), - mock.call.aspirate(50, rate=1.0) - ] - ) - - def test_tip_tracking_simple(self): - self.p200.move_to = mock.Mock() - self.p200.pick_up_tip() - self.p200.tip_attached = False # prior expectation, for test only - self.p200.pick_up_tip() - - assert self.p200.move_to.mock_calls == \ - self.build_pick_up_tip(self.p200, self.tiprack1[0]) + \ - self.build_pick_up_tip(self.p200, self.tiprack1[1]) - - def test_simulate_plunger_while_enqueing(self): - - self.p200.pick_up_tip() - self.assertEqual(self.p200.current_volume, 0) - - self.p200.aspirate(200) - self.assertEqual(self.p200.current_volume, 200) - - self.p200.dispense(20) - self.assertEqual(self.p200.current_volume, 180) - - self.p200.dispense(20) - self.assertEqual(self.p200.current_volume, 160) - - self.p200.dispense(60) - self.assertEqual(self.p200.current_volume, 100) - - self.p200.dispense(100) - self.assertEqual(self.p200.current_volume, 0) - - self.p200.drop_tip() - - def test_tip_tracking_chain(self): - # TODO (ben 20171130): revise this test to make more sense in the - # context of required tip pick_up/drop sequencing, etc. - - total_tips_per_plate = 4 - - self.tiprack1 = generate_plate( - total_tips_per_plate, 2, (5, 5), (0, 0), 5) - self.tiprack2 = generate_plate( - total_tips_per_plate, 2, (5, 5), (0, 0), 5) - self.robot._deck['1'].add(self.tiprack1, 'tiprack1') - self.robot._deck['2'].add(self.tiprack2, 'tiprack2') - - self.p200 = Pipette( - self.robot, - mount='right', - tip_racks=[self.tiprack1, self.tiprack2], - trash_container=self.tiprack1, - name='pipette-for-transfer-tests', - max_volume=200, - ul_per_mm=18.5 - ) - - self.p200.move_to = mock.Mock() - - for _ in range(0, total_tips_per_plate * 2): - self.p200.pick_up_tip() - self.p200.tip_attached = False # prior expectation, for test only - - expected = [] - for i in range(0, total_tips_per_plate): - expected.extend(self.build_pick_up_tip(self.p200, self.tiprack1[i])) - for i in range(0, total_tips_per_plate): - expected.extend(self.build_pick_up_tip(self.p200, self.tiprack2[i])) - - self.assertEqual( - self.p200.move_to.mock_calls, - expected - ) - - # test then when we go over the total number of tips, - # Pipette raises a RuntimeWarning - self.robot.clear_commands() - self.p200.reset() - for _ in range(0, total_tips_per_plate * 2): - self.p200.pick_up_tip() - self.p200.tip_attached = False # prior expectation, for test only - - self.assertRaises(RuntimeWarning, self.p200.pick_up_tip) - - def test_tip_tracking_chain_multi_channel(self): - # TODO (ben 20171130): revise this test to make more sense in the - # context of required tip pick_up/drop sequencing, etc. - - p200_multi = Pipette( - self.robot, - trash_container=self.trash, - tip_racks=[self.tiprack1, self.tiprack2], - max_volume=200, - min_volume=10, # These are variable - mount='right', - channels=8, - ul_per_mm=18.5 - ) - - p200_multi.calibrate_plunger( - top=0, bottom=10, blow_out=12, drop_tip=13) - p200_multi.move_to = mock.Mock() - - for _ in range(0, 12 * 2): - p200_multi.pick_up_tip() - p200_multi.tip_attached = False # prior expectation, for test only - - expected = [] - for i in range(0, 12): - expected.extend( - self.build_pick_up_tip(p200_multi, self.tiprack1.cols[i])) - for i in range(0, 12): - expected.extend( - self.build_pick_up_tip(p200_multi, self.tiprack2.cols[i])) - - self.assertEqual( - p200_multi.move_to.mock_calls, - expected - ) - - def test_tip_tracking_start_at_tip(self): - self.p200.start_at_tip(self.tiprack1['B2']) - self.p200.pick_up_tip() - self.assertEqual(self.tiprack1['B2'], self.p200.current_tip()) - - def test_tip_tracking_return(self): - # Note: because this test mocks out `drop_tip`, as a side-effect - # `tip_attached` must be manually set as it would be under the - # `return_tip` callstack, making this tesk somewhat fragile - - self.p200.drop_tip = mock.Mock() - - self.p200.pick_up_tip() - self.p200.return_tip() - self.p200.tip_attached = False - - self.p200.pick_up_tip() - self.p200.return_tip() - - expected = [ - mock.call(self.tiprack1[0], home_after=True), - mock.call(self.tiprack1[1], home_after=True) - ] +def test_simulate_plunger_while_enqueing(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette - self.assertEqual(self.p200.drop_tip.mock_calls, expected) - - def test_direct_movement_within_well(self): - self.robot.move_to = mock.Mock() - self.p200.move_to(self.plate[0]) - self.p200.move_to(self.plate[0].top()) - self.p200.move_to(self.plate[0].bottom()) - self.p200.move_to(self.plate[1]) - self.p200.move_to(self.plate[2]) - self.p200.move_to(self.plate[2].bottom()) - - expected = [ - mock.call( - self.plate[0], instrument=self.p200, strategy='arc'), - mock.call( - self.plate[0].top(), instrument=self.p200, strategy='direct'), - mock.call( - self.plate[0].bottom(), instrument=self.p200, strategy='direct'), - mock.call( - self.plate[1], instrument=self.p200, strategy='arc'), - mock.call( - self.plate[2], instrument=self.p200, strategy='arc'), - mock.call( - self.plate[2].bottom(), instrument=self.p200, strategy='direct') - ] - self.assertEqual(self.robot.move_to.mock_calls, expected) - - def build_pick_up_tip(self, pipette, well): - return [ - mock.call(well.top()), - mock.call( - well.top(-pipette._pick_up_distance), strategy='direct'), - mock.call(well.top(), strategy='direct'), - mock.call( - well.top(-pipette._pick_up_distance - 1), strategy='direct'), - mock.call(well.top(), strategy='direct'), - mock.call( - well.top(-pipette._pick_up_distance - 2), strategy='direct'), - mock.call(well.top(), strategy='direct') - ] + p200.pick_up_tip() + assert p200.current_volume == 0 + + p200.aspirate(200) + assert p200.current_volume == 200 + + p200.dispense(20) + assert p200.current_volume == 180 + + p200.dispense(20) + assert p200.current_volume == 160 + + p200.dispense(60) + assert p200.current_volume == 100 + + p200.dispense(100) + assert p200.current_volume == 0 + + p200.drop_tip() + + +def test_tip_tracking_chain(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + # TODO (ben 20171130): revise this test to make more sense in the + # context of required tip pick_up/drop sequencing, etc. + + total_tips_per_plate = 4 + + tiprack1 = generate_plate( + total_tips_per_plate, 2, (5, 5), (0, 0), 5) + tiprack2 = generate_plate( + total_tips_per_plate, 2, (5, 5), (0, 0), 5) + robot._deck['1'].add(tiprack1, 'tiprack1') + robot._deck['2'].add(tiprack2, 'tiprack2') + + p200 = Pipette( + robot, + mount='right', + tip_racks=[tiprack1, tiprack2], + trash_container=tiprack1, + name='pipette-for-transfer-tests', + max_volume=200, + ul_per_mm=18.5 + ) + + p200.move_to = mock.Mock() + + for _ in range(0, total_tips_per_plate * 2): + p200.pick_up_tip() + p200.tip_attached = False # prior expectation, for test only + + expected = [] + for i in range(0, total_tips_per_plate): + expected.extend(build_pick_up_tip(p200, tiprack1[i])) + for i in range(0, total_tips_per_plate): + expected.extend(build_pick_up_tip(p200, tiprack2[i])) + + assert p200.move_to.mock_calls == expected + + # test then when we go over the total number of tips, + # Pipette raises a RuntimeWarning + robot.clear_commands() + p200.reset() + for _ in range(0, total_tips_per_plate * 2): + p200.pick_up_tip() + p200.tip_attached = False # prior expectation, for test only + + with pytest.raises(RuntimeWarning): + p200.pick_up_tip() + + +def test_tip_tracking_chain_multi_channel(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + # TODO (ben 20171130): revise this test to make more sense in the + # context of required tip pick_up/drop sequencing, etc. + + p200_multi = Pipette( + robot, + trash_container=trash, + tip_racks=[tiprack1, tiprack2], + max_volume=200, + min_volume=10, # These are variable + mount='right', + channels=8, + ul_per_mm=18.5 + ) + + p200_multi.calibrate_plunger( + top=0, bottom=10, blow_out=12, drop_tip=13) + p200_multi.move_to = mock.Mock() + + for _ in range(0, 12 * 2): + p200_multi.pick_up_tip() + p200_multi.tip_attached = False # prior expectation, for test only + + expected = [] + for i in range(0, 12): + expected.extend( + build_pick_up_tip(p200_multi, tiprack1.cols[i])) + for i in range(0, 12): + expected.extend( + build_pick_up_tip(p200_multi, tiprack2.cols[i])) + + assert p200_multi.move_to.mock_calls == expected + + +def test_tip_tracking_start_at_tip(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + p200.start_at_tip(tiprack1['B2']) + p200.pick_up_tip() + assert tiprack1['B2'] == p200.current_tip() + + +def test_tip_tracking_return(local_test_pipette): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + # Note: because this test mocks out `drop_tip`, as a side-effect + # `tip_attached` must be manually set as it would be under the + # `return_tip` callstack, making this tesk somewhat fragile + + p200.drop_tip = mock.Mock() + + p200.pick_up_tip() + p200.return_tip() + p200.tip_attached = False + + p200.pick_up_tip() + p200.return_tip() + + expected = [ + mock.call(tiprack1[0], home_after=True), + mock.call(tiprack1[1], home_after=True) + ] + + assert p200.drop_tip.mock_calls == expected + + +def test_direct_movement_within_well(local_test_pipette, robot): + trash, tiprack1, tiprack2, plate, p200 = local_test_pipette + robot.move_to = mock.Mock() + p200.move_to(plate[0]) + p200.move_to(plate[0].top()) + p200.move_to(plate[0].bottom()) + p200.move_to(plate[1]) + p200.move_to(plate[2]) + p200.move_to(plate[2].bottom()) + + expected = [ + mock.call( + plate[0], instrument=p200, strategy='arc'), + mock.call( + plate[0].top(), instrument=p200, strategy='direct'), + mock.call( + plate[0].bottom(), instrument=p200, strategy='direct'), + mock.call( + plate[1], instrument=p200, strategy='arc'), + mock.call( + plate[2], instrument=p200, strategy='arc'), + mock.call( + plate[2].bottom(), instrument=p200, strategy='direct') + ] + assert robot.move_to.mock_calls == expected + + +def build_pick_up_tip(pipette, well): + return [ + mock.call(well.top()), + mock.call( + well.top(-pipette._pick_up_distance), strategy='direct'), + mock.call(well.top(), strategy='direct'), + mock.call( + well.top(-pipette._pick_up_distance - 1), strategy='direct'), + mock.call(well.top(), strategy='direct'), + mock.call( + well.top(-pipette._pick_up_distance - 2), strategy='direct'), + mock.call(well.top(), strategy='direct') + ] diff --git a/api/tests/opentrons/util/test_vector.py b/api/tests/opentrons/util/test_vector.py index 2aef314f381..c08e770fd14 100644 --- a/api/tests/opentrons/util/test_vector.py +++ b/api/tests/opentrons/util/test_vector.py @@ -1,83 +1,92 @@ -import unittest +import json + +import pytest from opentrons.util.vector import (Vector, VectorEncoder, VectorValue) -import json -class VectorTestCase(unittest.TestCase): - def test_init(self): - v1 = Vector(1, 2, 3) - v2 = Vector((1, 2, 3)) - v3 = Vector({'x': 1, 'y': 2, 'z': 3}) - v4 = Vector({'x': 1}) +def test_init(): + v1 = Vector(1, 2, 3) + v2 = Vector((1, 2, 3)) + v3 = Vector({'x': 1, 'y': 2, 'z': 3}) + v4 = Vector({'x': 1}) + + assert v1 == (1, 2, 3) + assert v2 == (1, 2, 3) + assert v3 == (1, 2, 3) + assert v4 == Vector(1, 0, 0) + + with pytest.raises(ValueError): + Vector() + + +def test_repr(): + v1 = Vector(1, 2, 3) + assert str(v1) == '(x=1.00, y=2.00, z=3.00)' + + +def test_add(): + v1 = Vector(1, 2, 3) + v2 = Vector(4, 5, 6) + res = v1 + v2 + + assert res == Vector(5, 7, 9) + - self.assertEqual(v1, (1, 2, 3)) - self.assertEqual(v2, (1, 2, 3)) - self.assertEqual(v3, (1, 2, 3)) - self.assertEqual(v4, Vector(1, 0, 0)) +def test_to_iterable(): + v1 = Vector(1, 2, 3) + iterable = v1.to_iterable() + assert hasattr(iterable, '__iter__') - self.assertRaises(ValueError, Vector) - def test_repr(self): - v1 = Vector(1, 2, 3) - self.assertEqual(str(v1), '(x=1.00, y=2.00, z=3.00)') +def test_zero_coordinates(): + zero_coords = Vector(1, 2, 3).zero_coordinates() + assert zero_coords == VectorValue(0, 0, 0) - def test_add(self): - v1 = Vector(1, 2, 3) - v2 = Vector(4, 5, 6) - res = v1 + v2 - self.assertEqual(res, Vector(5, 7, 9)) +def test_substract(): + v1 = Vector(1, 2, 3) + v2 = Vector(4, 5, 6) + res = v2 - v1 - def test_to_iterable(self): + assert res == Vector(3.0, 3.0, 3.0) - v1 = Vector(1, 2, 3) - iterable = v1.to_iterable() - self.assertTrue(hasattr(iterable, '__iter__')) - def test_zero_coordinates(self): +def test_index(): + v1 = Vector(1, 2, 3) - zero_coords = Vector(1, 2, 3).zero_coordinates() - self.assertEqual(zero_coords, VectorValue(0, 0, 0)) + assert v1['x'] == 1 + assert v1[0] == 1 + assert tuple(v1[:-1]) == (1, 2) - def test_substract(self): - v1 = Vector(1, 2, 3) - v2 = Vector(4, 5, 6) - res = v2 - v1 - self.assertEqual(res, Vector(3.0, 3.0, 3.0)) +def test_iterator(): + v1 = Vector(1, 2, 3) - def test_index(self): - v1 = Vector(1, 2, 3) + res = tuple([x for x in v1]) + assert res == (1, 2, 3) - self.assertEqual(v1['x'], 1) - self.assertEqual(v1[0], 1) - self.assertEqual(tuple(v1[:-1]), (1, 2)) - def test_iterator(self): - v1 = Vector(1, 2, 3) +def test_div(): + v1 = Vector(2.0, 4.0, 6.0) + res = v1 / 2.0 - res = tuple([x for x in v1]) - self.assertEqual(res, (1, 2, 3)) + assert res == Vector(1.0, 2.0, 3.0) + res = v1 / Vector(2.0, 4.0, 6.0) + assert res == Vector(1.0, 1.0, 1.0) - def test_div(self): - v1 = Vector(2.0, 4.0, 6.0) - res = v1 / 2.0 - self.assertEqual(res, Vector(1.0, 2.0, 3.0)) - res = v1 / Vector(2.0, 4.0, 6.0) - self.assertEqual(res, Vector(1.0, 1.0, 1.0)) +def test_mul(): + v1 = Vector(2.0, 4.0, 6.0) + res = v1 * 2.0 + assert res == Vector(4.0, 8.0, 12.0) - def test_mul(self): - v1 = Vector(2.0, 4.0, 6.0) - res = v1 * 2.0 - self.assertEqual(res, Vector(4.0, 8.0, 12.0)) + res = v1 * Vector(-1.0, -1.0, -1.0) + assert res == Vector(-2.0, -4.0, -6.0) - res = v1 * Vector(-1.0, -1.0, -1.0) - self.assertEqual(res, Vector(-2.0, -4.0, -6.0)) - def test_json_encoder(self): - v1 = Vector(1.0, 2.0, 3.0) - s = json.dumps(v1, cls=VectorEncoder) - v2 = json.loads(s) - self.assertEqual(v1, v2) +def test_json_encoder(): + v1 = Vector(1.0, 2.0, 3.0) + s = json.dumps(v1, cls=VectorEncoder) + v2 = json.loads(s) + assert v1 == v2