def setUp(self): self.s = PositionPlugin("S", "PRE") self.s.loop_run() self.positions = [ ("y", VDouble, np.repeat(np.arange(6, 9), 5) * 0.1, "mm"), ("x", VDouble, np.tile(np.arange(5), 3) * 0.1, "mm"), ("y_index", VInt, np.repeat(np.arange(3, dtype=numpy.int32), 5), ""), ("x_index", VInt, np.tile(np.arange(5, dtype=numpy.int32), 3), ""), ] self.in_params = dict(positions=self.positions) self.valid_params = dict( positions=self.positions, idStart=1, resetTimeout=1, runTime=None, runTimeout=1, abortTimeout=1, configureTimeout=1, arrayPort=None, ) self.send_params = dict(enableCallbacks=1, idStart=1, xml="something") self.maxDiff = 3000
class PositionPluginTest(unittest.TestCase): @patch("malcolm.devices.positionplugin.PvAttribute", DummyPVAttribute) def setUp(self): self.s = PositionPlugin("S", "PRE") self.s.loop_run() self.positions = [ ("y", VDouble, np.repeat(np.arange(6, 9), 5) * 0.1, "mm"), ("x", VDouble, np.tile(np.arange(5), 3) * 0.1, "mm"), ("y_index", VInt, np.repeat(np.arange(3, dtype=numpy.int32), 5), ""), ("x_index", VInt, np.tile(np.arange(5, dtype=numpy.int32), 3), ""), ] self.in_params = dict(positions=self.positions) self.valid_params = dict( positions=self.positions, idStart=1, resetTimeout=1, runTime=None, runTimeout=1, abortTimeout=1, configureTimeout=1, arrayPort=None, ) self.send_params = dict(enableCallbacks=1, idStart=1, xml="something") self.maxDiff = 3000 def test_init(self): base = ["prefix", "uptime"] pvs = [ "arrayPort", "delete", "enableCallbacks", "idStart", "portName", "positions", "running", "uniqueId", "xml", ] self.assertEqual(self.s.attributes.keys(), base + pvs) self.assertEqual(self.s.prefix, "PRE") for attr in pvs: self.assertEqual(self.s.attributes[attr].value, None) if hasattr(self.s.attributes[attr], "pv"): self.assertEqual(self.s.attributes[attr].pv.call_args, None) def test_validate(self): actual = self.s.validate(**self.in_params) self.assertEqual(actual, self.valid_params) def set_configured(self): # Set all the pvs to the right value for attr in sorted(self.send_params): self.s.attributes[attr]._value = self.send_params[attr] self.s.configure(block=False, **self.in_params) Attribute.update(self.s.attributes["delete"], True) cothread.Yield() Attribute.update(self.s.attributes["xml"], self.s._sconfig.seq_items[1].seq_params["xml"]) cothread.Yield() self.assertEqual(self.s.state, DState.Ready) def check_set(self, attr, expected): self.assertEqual(self.s.attributes[attr].pv.caput.call_count, 1, attr) call_args = self.s.attributes[attr].pv.caput.call_args val = call_args[0][0] if attr != "xml": self.assertEquals(val, expected, "{}: expected {} got {}".format(attr, expected, val)) Attribute.update(self.s.attributes[attr], val) self.s.attributes[attr].pv.reset_mock() def test_configure(self): spawned = cothread.Spawn(self.s.configure, **self.in_params) # Yield to let configure run cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Idle) # Yield to let do_config run cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Configuring) self.assertEqual(self.s.stateMachine.message, "Deleting old positions") self.check_set("delete", True) # Yield to let this post and then once to let the sm process cothread.Yield() for attr in sorted(self.send_params): self.check_set(attr, self.send_params[attr]) spawned.Wait(1) expected = """<?xml version="1.0" ?> <pos_layout> <dimensions> <dimension name="y"/> <dimension name="x"/> <dimension name="y_index"/> <dimension name="x_index"/> <dimension name="FilePluginClose"/> </dimensions> <positions> <position FilePluginClose="0" x="0.0" x_index="0" y="0.6" y_index="0"/> <position FilePluginClose="0" x="0.1" x_index="1" y="0.6" y_index="0"/> <position FilePluginClose="0" x="0.2" x_index="2" y="0.6" y_index="0"/> <position FilePluginClose="0" x="0.3" x_index="3" y="0.6" y_index="0"/> <position FilePluginClose="0" x="0.4" x_index="4" y="0.6" y_index="0"/> <position FilePluginClose="0" x="0.0" x_index="0" y="0.7" y_index="1"/> <position FilePluginClose="0" x="0.1" x_index="1" y="0.7" y_index="1"/> <position FilePluginClose="0" x="0.2" x_index="2" y="0.7" y_index="1"/> <position FilePluginClose="0" x="0.3" x_index="3" y="0.7" y_index="1"/> <position FilePluginClose="0" x="0.4" x_index="4" y="0.7" y_index="1"/> <position FilePluginClose="0" x="0.0" x_index="0" y="0.8" y_index="2"/> <position FilePluginClose="0" x="0.1" x_index="1" y="0.8" y_index="2"/> <position FilePluginClose="0" x="0.2" x_index="2" y="0.8" y_index="2"/> <position FilePluginClose="0" x="0.3" x_index="3" y="0.8" y_index="2"/> <position FilePluginClose="1" x="0.4" x_index="4" y="0.8" y_index="2"/> </positions> </pos_layout> """ self.assert_xml(self.s.xml, expected) self.assertEqual(self.s.stateMachine.state, DState.Ready) def assert_xml(self, xml, expected): pretty = minidom.parseString(xml).toprettyxml(indent=" ") if expected != pretty: print print pretty message = "".join(difflib.unified_diff(expected.splitlines(True), pretty.splitlines(True))) self.fail("Output doesn't match expected: %s\n" % message) def test_run(self): self.set_configured() # Do a run spawned = cothread.Spawn(self.s.run) cothread.Yield() cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Running) self.check_set("running", 1) self.assertEqual(self.s.running, 1) Attribute.update(self.s.attributes["running"], False) cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Idle) spawned.Wait(1) self.assertEqual(self.s.stateMachine.state, DState.Idle) def test_mismatch(self): self.set_configured() Attribute.update(self.s.attributes["enableCallbacks"], False) self.assertEqual(self.s.stateMachine.state, DState.Ready) cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Idle) def test_abort(self): self.set_configured() spawned = cothread.Spawn(self.s.run) cothread.Yield() cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Running) self.check_set("running", 1) self.assertEqual(self.s.running, 1) aspawned = cothread.Spawn(self.s.abort) cothread.Yield() cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Aborting) self.check_set("running", 0) Attribute.update(self.s.attributes["running"], False) cothread.Yield() self.assertEqual(self.s.stateMachine.state, DState.Aborted) spawned.Wait(1) aspawned.Wait(1)