def test_add_dummy(): A = mw.Apparatus() dummy = mw.Dummy(name="dummy") A.add(dummy, mw.Vessel(), tube) P = mw.Protocol(A) with pytest.raises(ValueError): P.add(dummy, active=1) # should be a bool!
def test_add_valve(): A = mw.Apparatus() valve = mw.Valve(mapping={pump1: 1, pump2: 2}) bad_valve = mw.Valve() A.add([pump1, pump2], [valve, bad_valve], tube) P = mw.Protocol(A) expected = [ dict(start=0, stop=None, component=valve, params={"setting": 1}) ] # directly pass the pump object P.add(valve, setting=pump1) assert P.procedures == expected P.procedures = [] # using its name P.add(valve, setting="pump1") assert P.procedures == expected P.procedures = [] # using its port number P.add(valve, setting=1) assert P.procedures == expected with pytest.raises(ValueError): P.add(valve, setting=3) with pytest.raises(ValueError): P.add(bad_valve, setting=3)
def test_compile(): P = mw.Protocol(A) P.add([pump1, pump2], rate="10 mL/min", duration="5 min") assert P._compile() == { pump1: [ { "params": { "rate": "10 mL/min" }, "time": 0 }, { "params": { "rate": "0 mL/min" }, "time": 300 }, ], pump2: [ { "params": { "rate": "10 mL/min" }, "time": mw._ureg.parse_expression("0 seconds"), }, { "params": { "rate": "0 mL/min" }, "time": 300 }, ], } # if no stop times are given, duration inference should fail P = mw.Protocol(A) P.add([pump1, pump2], rate="10 mL/min") with pytest.raises(RuntimeError): P._compile()
def test_switching_rates(): # check switching between rates P = mw.Protocol(A) P.add([pump1, pump2], rate="10 mL/min", duration="5 min") P.add(pump1, rate="5 mL/min", start="5 min", stop="10 min") assert P._compile() == { pump1: [ { "params": { "rate": "10 mL/min" }, "time": 0 }, { "params": { "rate": "5 mL/min" }, "time": 300 }, { "params": { "rate": "0 mL/min" }, "time": 600 }, ], pump2: [ { "params": { "rate": "10 mL/min" }, "time": 0 }, { "params": { "rate": "0 mL/min" }, "time": 300 }, ], }
def test_json(): P = mw.Protocol(A) P.add([pump1, pump2], rate="10 mL/min", duration="5 min") assert json.loads(P.json()) == [ { "start": 0, "stop": 300, "component": "pump1", "params": { "rate": "10 mL/min" }, }, { "start": 0, "stop": 300, "component": "pump2", "params": { "rate": "10 mL/min" }, }, ]
def test_yaml(): P = mw.Protocol(A) P.add([pump1, pump2], rate="10 mL/min", duration="5 min") assert yaml.safe_load(P.yaml()) == json.loads(P.json())
def test_conflicting_continuous_procedures(): P = mw.Protocol(A) P.add(pump1, rate="5 mL/min") P.add(pump1, rate="2 mL/min") with pytest.raises(RuntimeError): P._compile()
def test_overlapping_procedures(): P = mw.Protocol(A) P.add(pump1, start="0 seconds", stop="5 seconds", rate="5 mL/min") P.add(pump1, start="2 seconds", stop="5 seconds", rate="2 mL/min") with pytest.raises(RuntimeError): P._compile()
def test_add(): P = mw.Protocol(A) procedure = { "component": pump1, "params": { "rate": "10 mL/min" }, "start": 0, "stop": 300, } # test using duration P.add(pump1, rate="10 mL/min", duration="5 min") assert P.procedures[0] == procedure # test adding with start and stop P.procedures = [] P.add(pump1, rate="10 mL/min", start="0 min", stop="5 min") assert P.procedures[0] == procedure # test adding with start and stop as timedeltas P.procedures = [] P.add(pump1, rate="10 mL/min", start=timedelta(seconds=0), stop=timedelta(minutes=5)) assert P.procedures[0] == procedure # test adding with duration as timedelta P.procedures = [] P.add(pump1, rate="10 mL/min", duration=timedelta(minutes=5)) assert P.procedures[0] == procedure P = mw.Protocol(A) with pytest.raises(KeyError): P.add(mw.Pump("not in apparatus"), rate="10 mL/min", duration="5 min") # adding a class, not an instance of it with pytest.raises(ValueError): P.add(mw.Pump, rate="10 mL/min", duration="5 min") # Not adding keyword args with pytest.raises(RuntimeError): P.add(pump1, duration="5 min") # Invalid keyword for component with pytest.raises(ValueError): P.add(pump1, active=False, duration="5 min") # Invalid dimensionality for kwarg with pytest.raises(ValueError): P.add(pump1, rate="5 mL", duration="5 min") # No unit with pytest.raises(ValueError): P.add(pump1, rate="5", duration="5 min") # Just the raw value without a unit with pytest.raises(ValueError): P.add(pump1, rate=5, duration="5 min") # Providing stop and duration should raise error with pytest.raises(RuntimeError): P.add(pump1, rate="5 mL/min", stop="5 min", duration="5 min") # stop time before start time with pytest.raises(ValueError): P.add([pump1, pump2], rate="10 mL/min", start="5 min", stop="4 min")
def test_unused_component(): # raise warning if component not used P = mw.Protocol(A) P.add(pump1, rate="10 mL/min", duration="5 min") with pytest.warns(UserWarning, match="not used"): P._compile()
def test_create_protocol(): # test naming assert mw.Protocol(A, name="testing").name == "testing" assert mw.Protocol(A).name == "Protocol_0" assert mw.Protocol(A).name == "Protocol_1"
pump = mw.DummyPump(name="Dummy pump") test = mw.DummySensor(name="test") test2 = mw.DummySensor(name="test2") test3 = mw.DummySensor(name="test3") test4 = mw.BrokenDummySensor(name="test4") tube = mw.Tube("1 foot", "1/16 in", "2/16 in", "PVC") # create apparatus A = mw.Apparatus() A.add([a, b, c], pump, tube) A.add(pump, [test, test2, test3, test4], tube) P = mw.Protocol(A, name="testing execution") P.add(pump, rate="5 mL/min", start="0 seconds", stop="1 secs") P.add([test, test2, test3, test4], rate="5 Hz", start="0 secs", stop="1 secs") # test both execution modes for dry_run in [True, False]: E = P.execute(confirm=True, dry_run=dry_run, log_file=None, data_file=None) assert len(E.data["test"]) >= 5 if dry_run: assert E.data["test"][0].data == "simulated read" assert pump.rate == mw._ureg.parse_expression(pump._base_state["rate"]) # test fast forward E = P.execute(confirm=True, dry_run=5, log_file=None, data_file=None) assert len(E.data["test"]) >= 1