@opentrons/shared-data 0.1.7-alpha.1 → 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.coveragerc +4 -0
  2. package/.flake8 +31 -0
  3. package/Config.in +11 -0
  4. package/LICENSE +202 -0
  5. package/Makefile +13 -14
  6. package/Makefile-python.mk +134 -0
  7. package/buildroot.mk +35 -0
  8. package/command/index.ts +3 -1
  9. package/command/schemas/13.json +147 -315
  10. package/command/schemas/14.json +7254 -0
  11. package/command/types/index.ts +15 -4
  12. package/command/types/module.ts +24 -4
  13. package/command/types/setup.ts +5 -5
  14. package/command/types/support.ts +1 -0
  15. package/deck/definitions/5/ot2_short_trash.json +37 -0
  16. package/deck/definitions/5/ot2_standard.json +37 -0
  17. package/deck/definitions/5/ot3_standard.json +152 -51
  18. package/deck/schemas/5.json +46 -1
  19. package/labware/definitions/2/appliedbiosystemsmicroamp_384_wellplate_40ul/3.json +4737 -0
  20. package/labware/definitions/2/axygen_96_wellplate_500ul/1.json +1036 -0
  21. package/labware/definitions/2/axygen_96_wellplate_500ul/2.json +1159 -0
  22. package/labware/definitions/2/biorad_384_wellplate_50ul/4.json +4748 -0
  23. package/labware/definitions/2/biorad_96_wellplate_200ul_pcr/4.json +1187 -0
  24. package/labware/definitions/2/corning_12_wellplate_6.9ml_flat/4.json +210 -0
  25. package/labware/definitions/2/corning_24_wellplate_3.4ml_flat/4.json +342 -0
  26. package/labware/definitions/2/corning_48_wellplate_1.6ml_flat/5.json +610 -0
  27. package/labware/definitions/2/corning_6_wellplate_16.8ml_flat/4.json +134 -0
  28. package/labware/definitions/2/corning_96_wellplate_360ul_flat/4.json +1177 -0
  29. package/labware/definitions/2/corning_96_wellplate_360ul_lid/1.json +111 -0
  30. package/labware/definitions/2/ev_resin_tips_flex_96_labware/1.json +1 -1
  31. package/labware/definitions/2/ibidi_96_square_well_plate_300ul/1.json +1105 -0
  32. package/labware/definitions/2/ibidi_96_square_well_plate_300ul/2.json +1216 -0
  33. package/labware/definitions/2/ibidi_96_square_well_plate_300ul_lid/1.json +110 -0
  34. package/labware/definitions/2/nest_12_reservoir_22ml/1.json +232 -0
  35. package/labware/definitions/2/nest_24_wellplate_10.4ml/1.json +386 -0
  36. package/labware/definitions/2/nest_8_reservoir_22ml/1.json +162 -0
  37. package/labware/definitions/2/nest_96_wellplate_100ul_pcr_full_skirt/4.json +1166 -0
  38. package/labware/definitions/2/nest_96_wellplate_200ul_flat/4.json +1145 -0
  39. package/labware/definitions/2/nest_96_wellplate_2ml_deep/4.json +1261 -0
  40. package/labware/definitions/2/opentrons_96_wellplate_200ul_pcr_full_skirt/4.json +1174 -0
  41. package/labware/definitions/2/opentrons_flex_96_filtertiprack_1000ul/1.json +5 -0
  42. package/labware/definitions/2/opentrons_flex_96_filtertiprack_200ul/1.json +5 -0
  43. package/labware/definitions/2/opentrons_flex_96_filtertiprack_20ul/1.json +5 -0
  44. package/labware/definitions/2/opentrons_flex_96_filtertiprack_50ul/1.json +5 -0
  45. package/labware/definitions/2/opentrons_flex_96_tiprack_1000ul/1.json +5 -0
  46. package/labware/definitions/2/opentrons_flex_96_tiprack_200ul/1.json +5 -0
  47. package/labware/definitions/2/opentrons_flex_96_tiprack_20ul/1.json +5 -0
  48. package/labware/definitions/2/opentrons_flex_96_tiprack_50ul/1.json +5 -0
  49. package/labware/definitions/2/opentrons_flex_tiprack_lid/1.json +8 -8
  50. package/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json +8 -0
  51. package/labware/definitions/2/opentrons_tough_universal_lid/1.json +16 -1
  52. package/labware/definitions/2/smc_384_read_plate/1.json +4701 -0
  53. package/labware/definitions/2/smc_384_read_plate/2.json +5100 -0
  54. package/labware/definitions/2/thermoscientificnunc_96_wellplate_1300ul/3.json +1141 -0
  55. package/labware/definitions/2/thermoscientificnunc_96_wellplate_2000ul/3.json +1141 -0
  56. package/labware/definitions/2/usascientific_96_wellplate_2.4ml_deep/3.json +1243 -0
  57. package/labware/definitions/3/schema3test_96_well_aluminum_block/999.json +5 -2
  58. package/labware/definitions/3/schema3test_96_wellplate_200ul_pcr_full_skirt/999.json +15 -2
  59. package/labware/definitions/3/schema3test_aluminum_flat_bottom_plate/999.json +70 -0
  60. package/labware/definitions/3/schema3test_flex_96_tiprack_200ul/999.json +5 -9
  61. package/labware/definitions/3/schema3test_flex_96_tiprack_adapter/999.json +70 -0
  62. package/labware/definitions/3/schema3test_flex_tiprack_lid/999.json +146 -0
  63. package/labware/definitions/3/schema3test_tough_pcr_auto_sealing_lid/999.json +162 -0
  64. package/labware/definitions/3/schema3test_universal_flat_adapter/999.json +157 -0
  65. package/labware/fixtures/2/fixture_user_volumes_prototype.json +1137 -0
  66. package/labware/fixtures/3/fixture_2_plate.json +5 -2
  67. package/labware/fixtures/3/fixture_corning_24_plate.json +6 -3
  68. package/labware/fixtures/3/index.ts +4 -0
  69. package/labware/schemas/2.json +36 -1
  70. package/labware/schemas/3.json +57 -5
  71. package/lib/command/index.d.ts +39 -1
  72. package/lib/command/schemas/13.json +147 -315
  73. package/lib/command/schemas/14.json +7254 -0
  74. package/lib/command/types/index.d.ts +11 -3
  75. package/lib/command/types/module.d.ts +19 -6
  76. package/lib/command/types/setup.d.ts +5 -5
  77. package/lib/deck/definitions/5/ot2_short_trash.json +37 -0
  78. package/lib/deck/definitions/5/ot2_standard.json +37 -0
  79. package/lib/deck/definitions/5/ot3_standard.json +152 -51
  80. package/lib/deck/schemas/5.json +46 -1
  81. package/lib/index.mjs +240842 -193770
  82. package/lib/js/constants.d.ts +30 -9
  83. package/lib/js/fixtures.d.ts +77 -2
  84. package/lib/js/getLabware.d.ts +3 -3
  85. package/lib/js/helpers/__tests__/getMaxPushOutVolume.test.d.ts +1 -0
  86. package/lib/js/helpers/__tests__/getStackedItemsOnStartingDeck.test.d.ts +1 -0
  87. package/lib/js/helpers/__tests__/labwareSchemaShims.test.d.ts +1 -0
  88. package/lib/js/helpers/__tests__/pairsFromArray.test.d.ts +1 -0
  89. package/lib/js/helpers/__tests__/vectorMath.test.d.ts +1 -0
  90. package/lib/js/helpers/getFixedTrashLabwareDefinition.d.ts +2 -2
  91. package/lib/js/helpers/getLabwareDefURI.d.ts +3 -0
  92. package/lib/js/helpers/getLabwareDefinitionsByURIForProtocol.d.ts +6 -0
  93. package/lib/js/helpers/getLabwareInfoByLiquidId.d.ts +10 -0
  94. package/lib/js/helpers/getLiquidsByIdForLabware.d.ts +2 -0
  95. package/lib/js/helpers/getLoadedLabwareDefinitionsByUri.d.ts +2 -2
  96. package/lib/js/helpers/getMaxPushOutVolume.d.ts +2 -0
  97. package/lib/js/helpers/getStackedItemsOnStartingDeck.d.ts +50 -0
  98. package/lib/js/helpers/getStandardDeckViewLayerBlockList.d.ts +2 -0
  99. package/lib/js/helpers/getTipTypeFromTipRackDefinition.d.ts +2 -2
  100. package/lib/js/helpers/getWellFillFromLabwareId.d.ts +4 -0
  101. package/lib/js/helpers/getWellNamePerMultiTip.d.ts +3 -3
  102. package/lib/js/helpers/getWellTotalVolume.d.ts +2 -2
  103. package/lib/js/helpers/index.d.ts +22 -14
  104. package/lib/js/helpers/labwareInference.d.ts +3 -3
  105. package/lib/js/helpers/liquidClasses.d.ts +4 -2
  106. package/lib/js/helpers/matrixMath.d.ts +3 -0
  107. package/lib/js/helpers/pairsFromArray.d.ts +8 -0
  108. package/lib/js/helpers/parseProtocolCommands.d.ts +4 -4
  109. package/lib/js/helpers/positionMath.d.ts +127 -0
  110. package/lib/js/helpers/validateCustomLabwareHelper.d.ts +2 -2
  111. package/lib/js/helpers/vectorMath.d.ts +14 -0
  112. package/lib/js/helpers/wellSets.d.ts +4 -4
  113. package/lib/js/labware.d.ts +4 -3
  114. package/lib/js/liquidClasses.d.ts +4 -0
  115. package/lib/js/modules.d.ts +1 -1
  116. package/lib/js/types.d.ts +62 -13
  117. package/lib/labware/definitions/2/appliedbiosystemsmicroamp_384_wellplate_40ul/3.json +4737 -0
  118. package/lib/labware/definitions/2/axygen_96_wellplate_500ul/1.json +1036 -0
  119. package/lib/labware/definitions/2/axygen_96_wellplate_500ul/2.json +1159 -0
  120. package/lib/labware/definitions/2/biorad_384_wellplate_50ul/4.json +4748 -0
  121. package/lib/labware/definitions/2/biorad_96_wellplate_200ul_pcr/4.json +1187 -0
  122. package/lib/labware/definitions/2/corning_12_wellplate_6.9ml_flat/4.json +210 -0
  123. package/lib/labware/definitions/2/corning_24_wellplate_3.4ml_flat/4.json +342 -0
  124. package/lib/labware/definitions/2/corning_48_wellplate_1.6ml_flat/5.json +610 -0
  125. package/lib/labware/definitions/2/corning_6_wellplate_16.8ml_flat/4.json +134 -0
  126. package/lib/labware/definitions/2/corning_96_wellplate_360ul_flat/4.json +1177 -0
  127. package/lib/labware/definitions/2/corning_96_wellplate_360ul_lid/1.json +111 -0
  128. package/lib/labware/definitions/2/ev_resin_tips_flex_96_labware/1.json +1 -1
  129. package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul/1.json +1105 -0
  130. package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul/2.json +1216 -0
  131. package/lib/labware/definitions/2/ibidi_96_square_well_plate_300ul_lid/1.json +110 -0
  132. package/lib/labware/definitions/2/nest_12_reservoir_22ml/1.json +232 -0
  133. package/lib/labware/definitions/2/nest_24_wellplate_10.4ml/1.json +386 -0
  134. package/lib/labware/definitions/2/nest_8_reservoir_22ml/1.json +162 -0
  135. package/lib/labware/definitions/2/nest_96_wellplate_100ul_pcr_full_skirt/4.json +1166 -0
  136. package/lib/labware/definitions/2/nest_96_wellplate_200ul_flat/4.json +1145 -0
  137. package/lib/labware/definitions/2/nest_96_wellplate_2ml_deep/4.json +1261 -0
  138. package/lib/labware/definitions/2/opentrons_96_wellplate_200ul_pcr_full_skirt/4.json +1174 -0
  139. package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_1000ul/1.json +5 -0
  140. package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_200ul/1.json +5 -0
  141. package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_20ul/1.json +5 -0
  142. package/lib/labware/definitions/2/opentrons_flex_96_filtertiprack_50ul/1.json +5 -0
  143. package/lib/labware/definitions/2/opentrons_flex_96_tiprack_1000ul/1.json +5 -0
  144. package/lib/labware/definitions/2/opentrons_flex_96_tiprack_200ul/1.json +5 -0
  145. package/lib/labware/definitions/2/opentrons_flex_96_tiprack_20ul/1.json +5 -0
  146. package/lib/labware/definitions/2/opentrons_flex_96_tiprack_50ul/1.json +5 -0
  147. package/lib/labware/definitions/2/opentrons_flex_tiprack_lid/1.json +8 -8
  148. package/lib/labware/definitions/2/opentrons_tough_pcr_auto_sealing_lid/1.json +8 -0
  149. package/lib/labware/definitions/2/opentrons_tough_universal_lid/1.json +16 -1
  150. package/lib/labware/definitions/2/smc_384_read_plate/1.json +4701 -0
  151. package/lib/labware/definitions/2/smc_384_read_plate/2.json +5100 -0
  152. package/lib/labware/definitions/2/thermoscientificnunc_96_wellplate_1300ul/3.json +1141 -0
  153. package/lib/labware/definitions/2/thermoscientificnunc_96_wellplate_2000ul/3.json +1141 -0
  154. package/lib/labware/definitions/2/usascientific_96_wellplate_2.4ml_deep/3.json +1243 -0
  155. package/lib/labware/definitions/3/schema3test_96_well_aluminum_block/999.json +5 -2
  156. package/lib/labware/definitions/3/schema3test_96_wellplate_200ul_pcr_full_skirt/999.json +15 -2
  157. package/lib/labware/definitions/3/schema3test_aluminum_flat_bottom_plate/999.json +70 -0
  158. package/lib/labware/definitions/3/schema3test_flex_96_tiprack_200ul/999.json +5 -9
  159. package/lib/labware/definitions/3/schema3test_flex_96_tiprack_adapter/999.json +70 -0
  160. package/lib/labware/definitions/3/schema3test_flex_tiprack_lid/999.json +146 -0
  161. package/lib/labware/definitions/3/schema3test_tough_pcr_auto_sealing_lid/999.json +162 -0
  162. package/lib/labware/definitions/3/schema3test_universal_flat_adapter/999.json +157 -0
  163. package/lib/labware/fixtures/2/fixture_user_volumes_prototype.json +1137 -0
  164. package/lib/labware/fixtures/3/fixture_2_plate.json +5 -2
  165. package/lib/labware/fixtures/3/fixture_corning_24_plate.json +6 -3
  166. package/lib/labware/fixtures/3/index.d.ts +3 -0
  167. package/lib/labware/schemas/2.json +36 -1
  168. package/lib/labware/schemas/3.json +57 -5
  169. package/lib/liquid-class/definitions/1/{ethanol_80.json → ethanol_80/1.json} +545 -390
  170. package/lib/liquid-class/definitions/1/{glycerol_50.json → glycerol_50/1.json} +370 -215
  171. package/lib/liquid-class/definitions/1/{water.json → water/1.json} +151 -62
  172. package/lib/liquid-class/fixtures/1/fixture_glycerol50.json +1 -0
  173. package/lib/liquid-class/schemas/1.json +6 -0
  174. package/lib/module/definitions/3/absorbanceReaderV1.json +20 -151
  175. package/lib/module/definitions/3/flexStackerModuleV1.json +29 -389
  176. package/lib/module/definitions/3/heaterShakerModuleV1.json +16 -357
  177. package/lib/module/definitions/3/magneticBlockV1.json +18 -149
  178. package/lib/module/definitions/3/magneticModuleV1.json +16 -399
  179. package/lib/module/definitions/3/magneticModuleV2.json +16 -399
  180. package/lib/module/definitions/3/temperatureModuleV1.json +16 -153
  181. package/lib/module/definitions/3/temperatureModuleV2.json +16 -153
  182. package/lib/module/definitions/3/thermocyclerModuleV1.json +16 -1340
  183. package/lib/module/definitions/3/thermocyclerModuleV2.json +18 -3617
  184. package/lib/module/schemas/3.json +109 -12
  185. package/lib/pipette/definitions/1/pipetteModelSpecs.json +336 -0
  186. package/lib/pipette/definitions/2/general/ninety_six_channel/p200/3_2.json +660 -0
  187. package/lib/pipette/definitions/2/geometry/ninety_six_channel/p200/3_2.json +309 -0
  188. package/lib/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json +28 -18
  189. package/lib/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_2.json +192 -0
  190. package/lib/protocol/types/schemaV8/index.d.ts +17 -1
  191. package/liquid-class/definitions/1/{ethanol_80.json → ethanol_80/1.json} +545 -390
  192. package/liquid-class/definitions/1/{glycerol_50.json → glycerol_50/1.json} +370 -215
  193. package/liquid-class/definitions/1/{water.json → water/1.json} +151 -62
  194. package/liquid-class/fixtures/1/fixture_glycerol50.json +1 -0
  195. package/liquid-class/schemas/1.json +6 -0
  196. package/module/definitions/3/absorbanceReaderV1.json +20 -151
  197. package/module/definitions/3/flexStackerModuleV1.json +29 -389
  198. package/module/definitions/3/heaterShakerModuleV1.json +16 -357
  199. package/module/definitions/3/magneticBlockV1.json +18 -149
  200. package/module/definitions/3/magneticModuleV1.json +16 -399
  201. package/module/definitions/3/magneticModuleV2.json +16 -399
  202. package/module/definitions/3/temperatureModuleV1.json +16 -153
  203. package/module/definitions/3/temperatureModuleV2.json +16 -153
  204. package/module/definitions/3/thermocyclerModuleV1.json +16 -1340
  205. package/module/definitions/3/thermocyclerModuleV2.json +18 -3617
  206. package/module/schemas/3.json +109 -12
  207. package/mypy.ini +22 -0
  208. package/package.json +1 -1
  209. package/pipette/definitions/1/pipetteModelSpecs.json +336 -0
  210. package/pipette/definitions/2/general/ninety_six_channel/p200/3_2.json +660 -0
  211. package/pipette/definitions/2/geometry/ninety_six_channel/p200/3_2.json +309 -0
  212. package/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json +28 -18
  213. package/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_2.json +192 -0
  214. package/protocol/types/schemaV8/index.ts +29 -1
  215. package/pyproject.toml +119 -0
  216. package/pytest.ini +8 -0
  217. package/python_tests/__init__.py +1 -0
  218. package/python_tests/command/test_known_schema_ids.py +9 -0
  219. package/python_tests/conftest.py +108 -0
  220. package/python_tests/deck/__init__.py +0 -0
  221. package/python_tests/deck/test_list_names.py +12 -0
  222. package/python_tests/deck/test_position.py +101 -0
  223. package/python_tests/deck/test_typechecks.py +23 -0
  224. package/python_tests/errors/__init__.py +1 -0
  225. package/python_tests/errors/test_error_completeness.py +19 -0
  226. package/python_tests/errors/test_exceptions.py +74 -0
  227. package/python_tests/gripper/__init__.py +0 -0
  228. package/python_tests/gripper/test_definition.py +30 -0
  229. package/python_tests/gripper/test_load.py +19 -0
  230. package/python_tests/labware/__init__.py +13 -0
  231. package/python_tests/labware/test_typechecks.py +80 -0
  232. package/python_tests/liquid_classes/__init__.py +0 -0
  233. package/python_tests/liquid_classes/test_liquid_class_values.py +59 -0
  234. package/python_tests/liquid_classes/test_load.py +45 -0
  235. package/python_tests/liquid_classes/test_validations.py +192 -0
  236. package/python_tests/module/__init__.py +20 -0
  237. package/python_tests/module/test_load.py +49 -0
  238. package/python_tests/module/test_typechecks.py +26 -0
  239. package/python_tests/pipette/__init__.py +0 -0
  240. package/python_tests/pipette/test_load_data.py +109 -0
  241. package/python_tests/pipette/test_max_flow_rates_per_volume.py +112 -0
  242. package/python_tests/pipette/test_mutable_configurations.py +467 -0
  243. package/python_tests/pipette/test_pipette_definition.py +183 -0
  244. package/python_tests/pipette/test_pipette_load_name_conversions.py +244 -0
  245. package/python_tests/pipette/test_typechecks.py +47 -0
  246. package/python_tests/pipette/test_validate_schema.py +198 -0
  247. package/python_tests/protocol/__init__.py +7 -0
  248. package/python_tests/protocol/test_protocol_schema_v6.py +108 -0
  249. package/python_tests/protocol/test_protocol_schema_v7.py +35 -0
  250. package/python_tests/protocol/test_protocol_schema_v8.py +17 -0
  251. package/python_tests/protocol/test_typechecks.py +30 -0
  252. package/python_tests/robot/__init__.py +0 -0
  253. package/python_tests/robot/test_typechecks.py +13 -0
  254. package/lib/js/cypressUtils.d.ts +0 -1
  255. package/lib/js/helpers/getVectorDifference.d.ts +0 -2
  256. package/lib/js/helpers/getVectorSum.d.ts +0 -2
  257. /package/lib/js/{helpers/__tests__/getVectorDifference.test.d.ts → __tests__/fixtures.test.d.ts} +0 -0
  258. /package/lib/js/helpers/__tests__/{getVectorSum.test.d.ts → getLabwareInfoByLiquidId.test.d.ts} +0 -0
@@ -0,0 +1,467 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Dict, Any, cast, Union, Generator
6
+
7
+ import pytest
8
+
9
+ from opentrons_shared_data.pipette import (
10
+ mutable_configurations,
11
+ types,
12
+ pipette_definition,
13
+ pipette_load_name_conversions as pip_conversions,
14
+ load_data,
15
+ )
16
+
17
+
18
+ TEST_SERIAL_NUMBER = "P50MV1520200304"
19
+ TestOverrideType = Dict[str, Union[float, int, bool]]
20
+
21
+
22
+ @pytest.fixture
23
+ def TMPFILE_DATA() -> Dict[str, Any]:
24
+ return {
25
+ "dropTipShake": True,
26
+ "doubleDropTip": True,
27
+ "model": "p50_multi_v1.5",
28
+ "quirks": {"pickUpPresses": True, "dropTipShake": True, "doubleDropTip": True},
29
+ "pickUpSpeed": {
30
+ "value": 5.0,
31
+ "min": 1,
32
+ "max": 100,
33
+ "units": "mm/s",
34
+ "type": "float",
35
+ "default": 30,
36
+ },
37
+ }
38
+
39
+
40
+ @pytest.fixture
41
+ def override_configuration_path(tmp_path: Path) -> Generator[Path, None, None]:
42
+ os.environ["OT_API_CONFIG_DIR"] = str(tmp_path)
43
+
44
+ tmp_path.mkdir(parents=True, exist_ok=True)
45
+ with_pip_path = tmp_path / Path("pipettes")
46
+ with_pip_path.mkdir(parents=True, exist_ok=True)
47
+ yield with_pip_path
48
+
49
+ del os.environ["OT_API_CONFIG_DIR"]
50
+
51
+
52
+ @pytest.fixture
53
+ def overrides_fixture(
54
+ override_configuration_path: Path, TMPFILE_DATA: Dict[str, Any]
55
+ ) -> types.MutableConfig:
56
+ with open(override_configuration_path / f"{TEST_SERIAL_NUMBER}.json", "w") as f:
57
+ json.dump(TMPFILE_DATA, f)
58
+ return types.MutableConfig.build(**TMPFILE_DATA["pickUpSpeed"], name="pickUpSpeed")
59
+
60
+
61
+ def test_load_old_overrides_regression(
62
+ TMPFILE_DATA: Dict[str, Any], override_configuration_path: Path
63
+ ) -> None:
64
+ TMPFILE_DATA["pickUpCurrent"] = {
65
+ "value": 0.15,
66
+ "min": 0.08,
67
+ "max": 0.2,
68
+ "units": "amps",
69
+ "type": "float",
70
+ "default": 0.1,
71
+ }
72
+ with open(override_configuration_path / "P20SV222021040709.json", "w") as f:
73
+ json.dump(TMPFILE_DATA, f)
74
+ configs = mutable_configurations.load_with_mutable_configurations(
75
+ pipette_definition.PipetteModelVersionType(
76
+ pipette_type=types.PipetteModelType.p20,
77
+ pipette_channels=types.PipetteChannelType.SINGLE_CHANNEL,
78
+ pipette_version=types.PipetteVersionType(2, 2),
79
+ oem_type=types.PipetteOEMType.OT,
80
+ ),
81
+ override_configuration_path,
82
+ "P20SV222021040709",
83
+ )
84
+ assert (
85
+ configs.pick_up_tip_configurations.press_fit.configuration_by_nozzle_map[
86
+ list(
87
+ configs.pick_up_tip_configurations.press_fit.configuration_by_nozzle_map.keys()
88
+ )[0]
89
+ ]["default"].current
90
+ == 0.15
91
+ )
92
+
93
+
94
+ def test_list_mutable_configs_unknown_pipette_id(
95
+ override_configuration_path: Path,
96
+ ) -> None:
97
+ """Test unknown pipette id mutable configs.
98
+
99
+ Test that a user receives a list of all possible mutable configurations
100
+ with the default value equal to the regular value.
101
+ """
102
+
103
+ found_configurations = mutable_configurations.list_mutable_configs(
104
+ TEST_SERIAL_NUMBER, override_configuration_path
105
+ )
106
+ for c in found_configurations:
107
+ if isinstance(c, str):
108
+ # model string, ignore
109
+ continue
110
+ if isinstance(c, types.QuirkConfig):
111
+ assert isinstance(c.value, bool)
112
+ else:
113
+ assert c.default == c.value
114
+
115
+
116
+ def test_list_mutable_configs_known_pipette_id(
117
+ overrides_fixture: types.MutableConfig, override_configuration_path: Path
118
+ ) -> None:
119
+ """Test known pipette id mutable configs.
120
+
121
+ Test that a user receives a list of all possible mutable configurations
122
+ with the expected overrides also listed.
123
+ """
124
+ found_configurations = mutable_configurations.list_mutable_configs(
125
+ TEST_SERIAL_NUMBER, override_configuration_path
126
+ )
127
+
128
+ for c in found_configurations:
129
+ if isinstance(c, str):
130
+ # model string, ignore
131
+ continue
132
+ if overrides_fixture.name == c.name:
133
+ assert c.value == overrides_fixture.value
134
+ elif isinstance(c, types.QuirkConfig):
135
+ assert isinstance(c.value, bool)
136
+ else:
137
+ assert c.default == c.value
138
+
139
+
140
+ @pytest.mark.parametrize(
141
+ argnames=["overrides_dict", "saved_dict"],
142
+ argvalues=[
143
+ [
144
+ {"pickUpCurrent": 0.5, "dropTipSpeed": 5, "dropTipShake": False},
145
+ {
146
+ "quirks": {"dropTipShake": False},
147
+ "pickUpCurrent": {
148
+ "value": 0.5,
149
+ "default": 0.8,
150
+ "units": "amps",
151
+ "type": "float",
152
+ "min": 0.1,
153
+ "max": 2.0,
154
+ },
155
+ "model": "p50_multi_v1.5",
156
+ "dropTipSpeed": {
157
+ "value": 5,
158
+ "default": 5.0,
159
+ "units": "mm/s",
160
+ "type": "float",
161
+ "min": 0.01,
162
+ "max": 30,
163
+ },
164
+ },
165
+ ]
166
+ ],
167
+ )
168
+ def test_save_new_overrides_new_file(
169
+ override_configuration_path: Path,
170
+ overrides_dict: TestOverrideType,
171
+ saved_dict: Dict[str, Any],
172
+ ) -> None:
173
+ mutable_configurations.save_overrides(
174
+ TEST_SERIAL_NUMBER, overrides_dict, override_configuration_path
175
+ )
176
+ with open(override_configuration_path / f"{TEST_SERIAL_NUMBER}.json") as f:
177
+ new_file = json.load(f)
178
+ assert saved_dict == new_file
179
+
180
+
181
+ @pytest.mark.parametrize(
182
+ argnames=["overrides_dict"],
183
+ argvalues=[
184
+ [{"pickUpCurrent": 1, "pickUpSpeed": 10, "dropTipShake": False}],
185
+ [{"pickUpCurrent": 2}],
186
+ ],
187
+ )
188
+ def test_save_new_overrides_update_file(
189
+ override_configuration_path: Path,
190
+ overrides_fixture: types.MutableConfig,
191
+ overrides_dict: TestOverrideType,
192
+ TMPFILE_DATA: Dict[str, Any],
193
+ ) -> None:
194
+ mutable_configurations.save_overrides(
195
+ TEST_SERIAL_NUMBER, overrides_dict, override_configuration_path
196
+ )
197
+ with open(override_configuration_path / f"{TEST_SERIAL_NUMBER}.json") as f:
198
+ new_file = json.load(f)
199
+
200
+ for k, v in overrides_dict.items():
201
+ if isinstance(v, bool):
202
+ TMPFILE_DATA["quirks"][k] = v
203
+ elif TMPFILE_DATA.get(k):
204
+ TMPFILE_DATA[k]["value"] = v
205
+
206
+ TMPFILE_DATA["pickUpCurrent"] = {
207
+ "default": 0.8,
208
+ "max": 2.0,
209
+ "min": 0.1,
210
+ "type": "float",
211
+ "units": "amps",
212
+ "value": overrides_dict["pickUpCurrent"],
213
+ }
214
+
215
+ del TMPFILE_DATA["quirks"]["pickUpPresses"]
216
+ assert TMPFILE_DATA == new_file
217
+
218
+
219
+ @pytest.mark.parametrize(
220
+ argnames=["overrides_dict"],
221
+ argvalues=[
222
+ [{"pickUpCurrent": 1231.213, "dropTipSpeed": 121, "dropTipShake": False}],
223
+ [{"quirk123": True}],
224
+ ],
225
+ )
226
+ def test_save_invalid_overrides(
227
+ overrides_fixture: types.MutableConfig,
228
+ override_configuration_path: Path,
229
+ overrides_dict: TestOverrideType,
230
+ TMPFILE_DATA: Dict[str, Any],
231
+ ) -> None:
232
+ with pytest.raises(ValueError):
233
+ mutable_configurations.save_overrides(
234
+ TEST_SERIAL_NUMBER, overrides_dict, override_configuration_path
235
+ )
236
+ with open(override_configuration_path / f"{TEST_SERIAL_NUMBER}.json") as f:
237
+ new_file = json.load(f)
238
+ assert TMPFILE_DATA == new_file
239
+
240
+
241
+ @pytest.mark.parametrize(
242
+ argnames=["pipette_model", "serial_number"],
243
+ argvalues=[
244
+ [
245
+ pip_conversions.convert_pipette_model(
246
+ cast(types.PipetteModel, "p1000_96_v3.3")
247
+ ),
248
+ "P1KHV3320230629",
249
+ ],
250
+ [
251
+ pip_conversions.convert_pipette_model(
252
+ cast(types.PipetteModel, "p50_multi_v1.5")
253
+ ),
254
+ TEST_SERIAL_NUMBER,
255
+ ],
256
+ ],
257
+ )
258
+ def test_load_with_overrides(
259
+ overrides_fixture: types.MutableConfig,
260
+ pipette_model: pipette_definition.PipetteModelVersionType,
261
+ serial_number: str,
262
+ override_configuration_path: Path,
263
+ ) -> None:
264
+ """Test that you can load configurations both with pre-existing overrides and non-pre-existing overrides."""
265
+ updated_configurations = mutable_configurations.load_with_mutable_configurations(
266
+ pipette_model, override_configuration_path, serial_number
267
+ )
268
+
269
+ loaded_base_configurations = load_data.load_definition(
270
+ pipette_model.pipette_type,
271
+ pipette_model.pipette_channels,
272
+ pipette_model.pipette_version,
273
+ pipette_model.oem_type,
274
+ )
275
+
276
+ if serial_number == TEST_SERIAL_NUMBER:
277
+ dict_loaded_configs = loaded_base_configurations.model_dump(by_alias=True)
278
+ for map_key in dict_loaded_configs["pickUpTipConfigurations"]["pressFit"][
279
+ "configurationsByNozzleMap"
280
+ ]:
281
+ for tip_key in dict_loaded_configs["pickUpTipConfigurations"]["pressFit"][
282
+ "configurationsByNozzleMap"
283
+ ][map_key]:
284
+ dict_loaded_configs["pickUpTipConfigurations"]["pressFit"][
285
+ "configurationsByNozzleMap"
286
+ ][map_key][tip_key]["speed"] = 5.0
287
+
288
+ updated_configurations_dict = updated_configurations.model_dump(by_alias=True)
289
+ assert set(dict_loaded_configs.pop("quirks")) == set(
290
+ updated_configurations_dict.pop("quirks")
291
+ )
292
+ assert updated_configurations_dict == dict_loaded_configs
293
+ else:
294
+ assert updated_configurations == loaded_base_configurations
295
+
296
+
297
+ def test_build_mutable_config_using_old_units() -> None:
298
+ """Test that MutableConfigs can build with old units."""
299
+ old_units_config = {
300
+ "value": 5,
301
+ "default": 5.0,
302
+ "units": "mm/s",
303
+ "type": "float",
304
+ "min": 0.01,
305
+ "max": 30,
306
+ }
307
+ assert (
308
+ types.MutableConfig.build(**old_units_config, name="dropTipSpeed") is not None # type: ignore
309
+ )
310
+
311
+
312
+ @pytest.mark.parametrize(
313
+ ("filename", "type", "channels", "version", "file_contents"),
314
+ # From https://opentrons.atlassian.net/browse/RQA-3676.
315
+ # These could probably be pared down.
316
+ [
317
+ (
318
+ "P20MV202020121412.json",
319
+ types.PipetteModelType.p20,
320
+ types.PipetteChannelType.EIGHT_CHANNEL,
321
+ types.PipetteVersionType(2, 0),
322
+ '{"model": "p20_multi_v2.0"}',
323
+ ),
324
+ (
325
+ "P3HSV1318071638.json",
326
+ types.PipetteModelType.p300,
327
+ types.PipetteChannelType.SINGLE_CHANNEL,
328
+ types.PipetteVersionType(1, 3),
329
+ '{"dropTipShake": true, "model": "p300_single_v1.3", "quirks": {"dropTipShake": true}, "top": {"value": 30.0, "default": 19.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "pickUpPresses": {"value": 3.0, "default": 3, "units": "presses", "type": "int", "min": 0, "max": 15}}',
330
+ ),
331
+ (
332
+ "P3HMV212021040004.json",
333
+ types.PipetteModelType.p300,
334
+ types.PipetteChannelType.EIGHT_CHANNEL,
335
+ types.PipetteVersionType(2, 1),
336
+ '{"needsUnstick": true, "model": "p300_multi_v2.1"}',
337
+ ),
338
+ (
339
+ "P20SV202020032604.json",
340
+ types.PipetteModelType.p20,
341
+ types.PipetteChannelType.SINGLE_CHANNEL,
342
+ types.PipetteVersionType(2, 0),
343
+ '{"model": "p20_single_v2.0"}',
344
+ ),
345
+ (
346
+ "P1KSV202019072441.json",
347
+ types.PipetteModelType.p1000,
348
+ types.PipetteChannelType.SINGLE_CHANNEL,
349
+ types.PipetteVersionType(2, 0),
350
+ '{"pickupTipShake": true, "model": "p1000_single_v2.0", "quirks": {"pickupTipShake": true}}',
351
+ ),
352
+ (
353
+ "P3HMV202021011105.json",
354
+ types.PipetteModelType.p300,
355
+ types.PipetteChannelType.EIGHT_CHANNEL,
356
+ types.PipetteVersionType(2, 0),
357
+ '{"needsUnstick": true, "model": "p300_multi_v2.0"}',
358
+ ),
359
+ (
360
+ "P20SV202019072527.json",
361
+ types.PipetteModelType.p20,
362
+ types.PipetteChannelType.SINGLE_CHANNEL,
363
+ types.PipetteVersionType(2, 0),
364
+ '{"model": "p20_single_v2.0"}',
365
+ ),
366
+ (
367
+ "P3HSV202021042602.json",
368
+ types.PipetteModelType.p300,
369
+ types.PipetteChannelType.SINGLE_CHANNEL,
370
+ types.PipetteVersionType(2, 0),
371
+ '{"model": "p300_single_v2.0"}',
372
+ ),
373
+ (
374
+ "P20SV222021030914.json",
375
+ types.PipetteModelType.p20,
376
+ types.PipetteChannelType.SINGLE_CHANNEL,
377
+ types.PipetteVersionType(2, 2),
378
+ '{"model": "p20_single_v2.2", "quirks": {}, "pickUpPresses": {"value": 3.0, "default": 1, "units": "presses", "type": "int", "min": 0, "max": 15}}',
379
+ ),
380
+ (
381
+ "P3HMV202020040801.json",
382
+ types.PipetteModelType.p300,
383
+ types.PipetteChannelType.EIGHT_CHANNEL,
384
+ types.PipetteVersionType(2, 0),
385
+ '{"needsUnstick": true, "model": "p300_multi_v2.0", "quirks": {"needsUnstick": true}, "top": {"value": 20.0, "default": 19.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "bottom": {"value": -14.0, "default": -14.5, "units": "mm", "type": "float", "min": -20, "max": 30}, "blowout": {"value": -18.5, "default": -19.0, "units": "mm", "type": "float", "min": -20, "max": 30}, "dropTip": {"value": -20.0, "default": -33.4, "units": "mm", "type": "float", "min": -20, "max": 30}, "pickUpCurrent": {"value": 0.9, "default": 0.8, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "pickUpDistance": {"value": 10.0, "default": 11.0, "units": "mm", "type": "float", "min": 0, "max": 10}, "pickUpIncrement": {"value": 0.1, "default": 0.0, "units": "mm", "type": "int", "min": 0, "max": 10}, "pickUpPresses": {"value": 4.0, "default": 1, "units": "presses", "type": "int", "min": 0, "max": 15}, "pickUpSpeed": {"value": 11.0, "default": 10.0, "units": "mm/s", "type": "float", "min": 0.01, "max": 30}, "plungerCurrent": {"value": 1.1, "default": 1.0, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "dropTipCurrent": {"value": 1.22, "default": 1.25, "units": "amps", "type": "float", "min": 0.1, "max": 2.0}, "dropTipSpeed": {"value": 8.0, "default": 7.5, "units": "mm/s", "type": "float", "min": 0.01, "max": 30}, "tipLength": {"value": 52.0, "default": 51.0, "units": "mm", "type": "float", "min": 0, "max": 100}}',
386
+ ),
387
+ (
388
+ "P3HMV212021040002.json",
389
+ types.PipetteModelType.p300,
390
+ types.PipetteChannelType.EIGHT_CHANNEL,
391
+ types.PipetteVersionType(2, 1),
392
+ '{"needsUnstick": true, "model": "p300_multi_v2.1"}',
393
+ ),
394
+ (
395
+ "P3HMV1318072625.json",
396
+ types.PipetteModelType.p300,
397
+ types.PipetteChannelType.EIGHT_CHANNEL,
398
+ types.PipetteVersionType(1, 3),
399
+ '{"dropTipShake": true, "model": "p300_multi_v1.3", "quirks": {"dropTipShake": true}, "pickUpPresses": {"value": 4.0, "default": 3, "units": "presses", "type": "int", "min": 0, "max": 15}}',
400
+ ),
401
+ (
402
+ "P50MV1519091757.json",
403
+ types.PipetteModelType.p50,
404
+ types.PipetteChannelType.EIGHT_CHANNEL,
405
+ types.PipetteVersionType(1, 5),
406
+ '{"dropTipShake": true, "doubleDropTip": true, "model": "p50_multi_v1.5", "quirks": {"doubleDropTip": true, "dropTipShake": true}}',
407
+ ),
408
+ (
409
+ "P3HSV202019072224.json",
410
+ types.PipetteModelType.p300,
411
+ types.PipetteChannelType.SINGLE_CHANNEL,
412
+ types.PipetteVersionType(2, 0),
413
+ '{"model": "p300_single_v2.0", "quirks": {}}',
414
+ ),
415
+ (
416
+ "P20MV202019112708.json",
417
+ types.PipetteModelType.p20,
418
+ types.PipetteChannelType.EIGHT_CHANNEL,
419
+ types.PipetteVersionType(2, 0),
420
+ '{"model": "p20_multi_v2.0", "quirks": {}}',
421
+ ),
422
+ (
423
+ "P3HSV202021031503.json",
424
+ types.PipetteModelType.p300,
425
+ types.PipetteChannelType.SINGLE_CHANNEL,
426
+ types.PipetteVersionType(2, 1),
427
+ '{"model": "p300_single_v2.1"}',
428
+ ),
429
+ (
430
+ "P1KSV202020060206.json",
431
+ types.PipetteModelType.p1000,
432
+ types.PipetteChannelType.SINGLE_CHANNEL,
433
+ types.PipetteVersionType(2, 0),
434
+ '{"pickupTipShake": true, "model": "p1000_single_v2.0"}',
435
+ ),
436
+ (
437
+ "P3HMV202021010906.json",
438
+ types.PipetteModelType.p300,
439
+ types.PipetteChannelType.EIGHT_CHANNEL,
440
+ types.PipetteVersionType(2, 0),
441
+ '{"needsUnstick": true, "model": "p300_multi_v2.0"}',
442
+ ),
443
+ ],
444
+ )
445
+ def test_loading_does_not_log_warnings(
446
+ filename: str,
447
+ type: types.PipetteModelType,
448
+ channels: types.PipetteChannelType,
449
+ version: types.PipetteVersionType,
450
+ file_contents: str,
451
+ caplog: pytest.LogCaptureFixture,
452
+ override_configuration_path: Path,
453
+ ) -> None:
454
+ """Make sure load_with_mutable_configurations() doesn't log any exceptions.
455
+
456
+ load_with_mutable_configurations() suppresses and logs internal exceptions to
457
+ protect its caller, but those are still bugs, and we still want tests to catch them.
458
+ """
459
+ model = pipette_definition.PipetteModelVersionType(
460
+ type, channels, version, types.PipetteOEMType.OT
461
+ )
462
+ (override_configuration_path / filename).write_text(file_contents)
463
+ with caplog.at_level(logging.WARNING):
464
+ mutable_configurations.load_with_mutable_configurations(
465
+ model, override_configuration_path, Path(filename).stem
466
+ )
467
+ assert len(caplog.records) == 0
@@ -0,0 +1,183 @@
1
+ import pytest
2
+ from typing import cast, List
3
+ from opentrons_shared_data.pipette.types import (
4
+ PipetteChannelType,
5
+ PipetteModelType,
6
+ PipetteVersionType,
7
+ PipetteModelMajorVersionType,
8
+ PipetteModelMinorVersionType,
9
+ LiquidClasses,
10
+ )
11
+ from opentrons_shared_data.pipette.pipette_definition import (
12
+ liquid_class_for_volume_between_default_and_defaultlowvolume,
13
+ PipetteLiquidPropertiesDefinition,
14
+ SupportedTipsDefinition,
15
+ )
16
+
17
+
18
+ def get_liquid_definition_for(
19
+ liquid_class: LiquidClasses,
20
+ ) -> PipetteLiquidPropertiesDefinition:
21
+ if liquid_class == LiquidClasses.lowVolumeDefault:
22
+ return PipetteLiquidPropertiesDefinition.model_validate(
23
+ {
24
+ "supportedTips": {
25
+ "t50": SupportedTipsDefinition.model_validate(
26
+ {
27
+ "defaultAspirateFlowRate": {
28
+ "default": 5,
29
+ "valuesByApiLevel": {"2.0": 5},
30
+ },
31
+ "defaultDispenseFlowRate": {
32
+ "default": 5,
33
+ "valuesByApiLevel": {"2.0": 5},
34
+ },
35
+ "defaultBlowOutFlowRate": {
36
+ "default": 10,
37
+ "valuesByApiLevel": {"2.0": 10},
38
+ },
39
+ "defaultFlowAcceleration": 100,
40
+ "defaultTipLength": 100,
41
+ "defaultReturnTipHeight": 0.5,
42
+ "defaultPushOutVolume": 5,
43
+ "aspirate": {"default": {"1": [[1, 2, 3]]}},
44
+ "dispense": {"default": {"1": [[1, 2, 3]]}},
45
+ }
46
+ )
47
+ },
48
+ "versionedTipOverlapDictionary": {"v0": {}},
49
+ "maxVolume": 30,
50
+ "minVolume": 1,
51
+ "defaultTipracks": [],
52
+ }
53
+ )
54
+ else:
55
+ return PipetteLiquidPropertiesDefinition.model_validate(
56
+ {
57
+ "supportedTips": {
58
+ "t50": SupportedTipsDefinition.model_validate(
59
+ {
60
+ "defaultAspirateFlowRate": {
61
+ "default": 5,
62
+ "valuesByApiLevel": {"2.0": 5},
63
+ },
64
+ "defaultDispenseFlowRate": {
65
+ "default": 5,
66
+ "valuesByApiLevel": {"2.0": 5},
67
+ },
68
+ "defaultBlowOutFlowRate": {
69
+ "default": 10,
70
+ "valuesByApiLevel": {"2.0": 10},
71
+ },
72
+ "defaultFlowAcceleration": 100,
73
+ "defaultTipLength": 100,
74
+ "defaultReturnTipHeight": 0.5,
75
+ "defaultPushOutVolume": 5,
76
+ "aspirate": {"default": {"1": [[1, 2, 3]]}},
77
+ "dispense": {"default": {"1": [[1, 2, 3]]}},
78
+ }
79
+ )
80
+ },
81
+ "versionedTipOverlapDictionary": {"v0": {}},
82
+ "maxVolume": 100,
83
+ "minVolume": 5,
84
+ "defaultTipracks": [],
85
+ }
86
+ )
87
+
88
+
89
+ @pytest.mark.parametrize(
90
+ argnames=["model", "expected_enum"],
91
+ argvalues=[["p50", PipetteModelType.p50], ["p1000", PipetteModelType.p1000]],
92
+ )
93
+ def test_model_enum(model: str, expected_enum: PipetteModelType) -> None:
94
+ assert expected_enum == PipetteModelType(model)
95
+
96
+
97
+ @pytest.mark.parametrize(argnames="channels", argvalues=[1, 8, 96])
98
+ def test_channel_enum(channels: int) -> None:
99
+ channel_type = PipetteChannelType(channels)
100
+ assert channels == channel_type
101
+
102
+
103
+ def test_incorrect_values() -> None:
104
+ with pytest.raises(ValueError):
105
+ PipetteModelType("p100")
106
+
107
+ with pytest.raises(ValueError):
108
+ PipetteChannelType(99)
109
+
110
+
111
+ @pytest.mark.parametrize(
112
+ argnames=["major", "minor"],
113
+ argvalues=[[1, 0], [1, 3], [3, 9]],
114
+ )
115
+ def test_version_enum(major: int, minor: int) -> None:
116
+ version_type = PipetteVersionType(
117
+ cast(PipetteModelMajorVersionType, major),
118
+ cast(PipetteModelMinorVersionType, minor),
119
+ )
120
+ assert version_type.as_tuple == (major, minor)
121
+
122
+
123
+ @pytest.mark.parametrize(
124
+ argnames=[
125
+ "all_liquid_classes",
126
+ "volume",
127
+ "current_liquid_class",
128
+ "expected_liquid_class",
129
+ ],
130
+ argvalues=[
131
+ [
132
+ [LiquidClasses.default],
133
+ 1,
134
+ LiquidClasses.lowVolumeDefault,
135
+ LiquidClasses.default,
136
+ ],
137
+ [
138
+ [LiquidClasses.lowVolumeDefault, LiquidClasses.default],
139
+ 1,
140
+ LiquidClasses.lowVolumeDefault,
141
+ LiquidClasses.lowVolumeDefault,
142
+ ],
143
+ [
144
+ [LiquidClasses.lowVolumeDefault, LiquidClasses.default],
145
+ 1,
146
+ LiquidClasses.default,
147
+ LiquidClasses.lowVolumeDefault,
148
+ ],
149
+ [
150
+ [LiquidClasses.lowVolumeDefault, LiquidClasses.default],
151
+ 5,
152
+ LiquidClasses.default,
153
+ LiquidClasses.default,
154
+ ],
155
+ [
156
+ [LiquidClasses.lowVolumeDefault, LiquidClasses.default],
157
+ 5,
158
+ LiquidClasses.lowVolumeDefault,
159
+ LiquidClasses.default,
160
+ ],
161
+ [
162
+ [LiquidClasses.lowVolumeDefault, LiquidClasses.default],
163
+ 100,
164
+ LiquidClasses.lowVolumeDefault,
165
+ LiquidClasses.default,
166
+ ],
167
+ ],
168
+ )
169
+ def test_liquid_class_for_volume_between_default_and_defaultlowvolume(
170
+ all_liquid_classes: List[LiquidClasses],
171
+ volume: float,
172
+ current_liquid_class: LiquidClasses,
173
+ expected_liquid_class: LiquidClasses,
174
+ ) -> None:
175
+ available_liquid_classes = {
176
+ lc: get_liquid_definition_for(lc) for lc in all_liquid_classes
177
+ }
178
+ assert (
179
+ liquid_class_for_volume_between_default_and_defaultlowvolume(
180
+ volume, current_liquid_class, available_liquid_classes
181
+ )
182
+ == expected_liquid_class
183
+ )