Esempio n. 1
0
class TestProcessController(unittest.TestCase):
    prefix = f"unitTest:{floor(time.time()).__repr__()[:-2]}"

    def setUp(self):
        self.config_dir = tmp_dir("config_dir")
        self.process = Process("proc")
        self.o = ProcessController("MyMRI", self.prefix, self.config_dir.value)
        self.process.add_controller(self.o)
        self.process.start()
        self.b = self.process.block_view("MyMRI")

    def tearDown(self):
        self.process.stop(timeout=2)
        shutil.rmtree(self.config_dir.value)

    def test_sets_stats(self):
        # In unit tests, this depends on where the test-runner is run from
        assert self.b.pymalcolmVer.value in ["work", __version__]
        hostname = os.uname()[1]
        hostname = hostname if len(hostname) < 39 else hostname[:35] + "..."
        assert self.b.hostname.value == hostname

    def test_starts_ioc(self):
        cothread.Sleep(5)
        assert catools.caget(self.prefix + ":PYMALCOLM:VER") in ["work", __version__]

    def test_ioc_ticks(self):
        cothread.Sleep(5)
        uptime = catools.caget(self.prefix + ":UPTIME:RAW")
        assert uptime >= 0
        time.sleep(5)
        assert catools.caget(self.prefix + ":UPTIME:RAW") >= uptime + 5
Esempio n. 2
0
class TestBasicController(unittest.TestCase):
    def setUp(self):
        self.process = Process("proc")
        self.o = BasicController("MyMRI")
        self.process.add_controller(self.o)
        self.process.start()
        self.b = self.process.block_view("MyMRI")

    def tearDown(self):
        self.process.stop(timeout=2)

    def update_health(self, num, alarm=Alarm.ok):
        self.o.update_health(num, HealthInfo(alarm))

    def test_set_health(self):
        self.update_health(1, Alarm(severity=AlarmSeverity.MINOR_ALARM))
        self.update_health(2, Alarm(severity=AlarmSeverity.MAJOR_ALARM))
        assert self.b.health.alarm.severity == AlarmSeverity.MAJOR_ALARM

        self.update_health(1, Alarm(severity=AlarmSeverity.UNDEFINED_ALARM))
        self.update_health(2, Alarm(severity=AlarmSeverity.INVALID_ALARM))
        assert self.b.health.alarm.severity == AlarmSeverity.UNDEFINED_ALARM

        self.update_health(1)
        self.update_health(2)
        assert self.o.health.value == "OK"
Esempio n. 3
0
class TestLabelPart(unittest.TestCase):
    def setUp(self):
        self.o = call_with_params(LabelPart,
                                  initialValue="My label",
                                  group="mygroup")
        self.p = Process("proc")
        self.c = Controller(self.p, "mri", [self.o])
        self.p.add_controller("mri", self.c)
        self.p.start()
        self.b = self.c.block_view()

    def tearDown(self):
        self.p.stop(1)

    def test_init(self):
        assert self.o.name == "label"
        assert self.o.attr.value == "My label"
        assert self.o.attr.meta.tags == ("widget:textinput", "config",
                                         "group:mygroup")
        assert self.b.meta.label == "My label"

    def test_setter(self):
        self.b.label.put_value("My label2")
        assert self.b.label.value == "My label2"
        assert self.b.meta.label == "My label2"
Esempio n. 4
0
class TestCSPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.child = self.create_child_block(cs_block,
                                             self.process,
                                             mri="PMAC:CS1",
                                             pv_prefix="PV:PRE")
        self.set_attributes(self.child, port="PMAC2CS1")
        c = ManagerController("PMAC", "/tmp")
        c.add_part(CSPart(mri="PMAC:CS1", cs=1))
        self.process.add_controller(c)
        self.process.start()
        self.b = c.block_view()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_init(self):
        assert "moveCS1" in self.b

    def test_move(self):
        self.mock_when_value_matches(self.child)
        # Move time is converted into milliseconds
        move_time = 2.3
        expected_move_time = move_time * 1000.0
        self.b.moveCS1(a=32, c=19.1, moveTime=move_time)
        assert self.child.handled_requests.mock_calls == [
            call.put("deferMoves", True),
            call.put("csMoveTime", expected_move_time),
            call.put("demandA", 32),
            call.put("demandC", 19.1),
            call.when_value_matches("demandA", 32, None),
            call.when_value_matches("demandC", 19.1, None),
            call.put("deferMoves", False),
        ]
Esempio n. 5
0
class TestMotorPreMovePart(ChildTestCase):
    def setUp(self):
        self.process = Process("test_process")
        self.context = Context(self.process)

        # Create a raw motor mock to handle axis request
        self.child = self.create_child_block(raw_motor_block,
                                             self.process,
                                             mri="BS",
                                             pv_prefix="PV:PRE")
        # Add Beam Selector object
        self.o = MotorPreMovePart(name="MotorPreMovePart", mri="BS", demand=50)

        controller = RunnableController("SCAN", "/tmp")
        controller.add_part(self.o)

        self.process.add_controller(controller)
        self.process.start()

    def tearDown(self):
        del self.context
        self.process.stop(timeout=1)

    def test_bs(self):
        b = self.context.block_view("SCAN")
        generator = CompoundGenerator([LineGenerator("x", "mm", 0, 1, 10)], [],
                                      [], 0.1)
        b.configure(generator)

        self.o.on_configure(self.context)

        assert self.child.handled_requests.mock_calls == [
            call.put("demand", 50)
        ]
Esempio n. 6
0
class TestHelloBlock(unittest.TestCase):
    def setUp(self):
        self.p = Process("proc")
        for c in hello_block("mri"):
            self.p.add_controller(c)
        self.p.start()

    def tearDown(self):
        self.p.stop()

    def test_say_hello(self):
        b = self.p.block_view("mri")
        expected = "Hello test_name"
        response = b.greet("test_name", 0)
        assert expected == response

    def test_method_meta(self):
        b = self.p.block_view("mri")
        method = b.greet.meta
        assert list(method.to_dict()) == [
            "typeid",
            "takes",
            "defaults",
            "description",
            "tags",
            "writeable",
            "label",
            "returns",
        ]
        assert method.defaults == dict(sleep=0.0)
        assert list(method.takes["elements"]) == ["name", "sleep"]
        assert list(method.returns["elements"]) == ["return"]
        assert method.tags == ["method:return:unpacked"]
        assert method.writeable
Esempio n. 7
0
class TestSystemPVACommsServerAndClient(unittest.TestCase):
    def setUp(self):
        self.mp_q = multiprocessing.Queue()
        self.mp = multiprocessing.Process(target=p1, args=(self.mp_q, ))
        self.mp.start()
        from malcolm.core import Process, call_with_params
        from malcolm.blocks.pva import pva_client_block
        self.process2 = Process("proc2")
        self.client = call_with_params(pva_client_block,
                                       self.process2,
                                       mri="client")
        self.process2.start()

    def tearDown(self):
        self.process2.stop(timeout=1)
        self.mp_q.put(None)
        self.mp.join()

    def s_server_hello_with_malcolm_client(self):
        from malcolm.core import call_with_params, Context, ResponseError
        from malcolm.blocks.builtin import proxy_block
        call_with_params(proxy_block,
                         self.process2,
                         mri="hello",
                         comms="client")
        context = Context(self.process2)
        context.when_matches(["hello", "health", "value"], "OK", timeout=2)
        block2 = self.process2.block_view("hello")
        ret = block2.greet(name="me2")
        assert ret == dict(greeting="Hello me2")
        with self.assertRaises(ResponseError):
            block2.error()
class TestSystemWSCommsServerOnly(unittest.TestCase):
    socket = 8881

    def setUp(self):
        self.sf = SyncFactory("sync")
        self.process = Process("proc", self.sf)
        part = HelloPart(self.process, None)
        DefaultController("hello", self.process, parts={"hello": part})
        WebsocketServerComms(self.process, dict(port=self.socket))
        self.process.start()

    def tearDown(self):
        self.process.stop()

    @gen.coroutine
    def send_message(self):
        conn = yield websocket_connect("ws://localhost:%s/ws" % self.socket)
        req = dict(type="malcolm:core/Post:1.0",
                   id=0,
                   endpoint=["hello", "say_hello"],
                   parameters=dict(name="me"))
        conn.write_message(json.dumps(req))
        resp = yield conn.read_message()
        resp = json.loads(resp)
        self.assertEqual(
            resp,
            dict(id=0,
                 type="malcolm:core/Return:1.0",
                 value=dict(greeting="Hello me")))
        conn.close()

    def test_server_and_simple_client(self):
        self.send_message()
Esempio n. 9
0
class TestCompoundPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        child = self.create_child_block(compound_motor_block,
                                        self.process,
                                        mri="my_mri",
                                        prefix="PV:PRE")
        self.set_attributes(child,
                            maxVelocity=5.0,
                            accelerationTime=0.5,
                            readback=12.3,
                            offset=4.5,
                            resolution=0.001,
                            cs="CS_PORT,B")
        self.o = MotorPart(name="scan", mri="my_mri")
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_report(self):
        returns = self.o.report_status(self.context)
        assert returns.cs_axis == "B"
        assert returns.cs_port == "CS_PORT"
        assert returns.acceleration == 10.0
        assert returns.resolution == 0.001
        assert returns.offset == 4.5
        assert returns.max_velocity == 5.0
        assert returns.current_position == 12.3
        assert returns.scannable == "scan"
Esempio n. 10
0
class TestCounterPart(unittest.TestCase):

    def setUp(self):
        self.p = Process("proc")
        c = BasicController("mri")
        c.add_part(CounterPart(name='counting'))
        self.p.add_controller(c)
        self.p.start()
        self.b = self.p.block_view("mri")

    def tearDown(self):
        self.p.stop()

    def test_increment_increments(self):
        assert 0 == self.b.counter.value
        self.b.increment()
        assert 1 == self.b.counter.value
        self.b.increment()
        assert 2 == self.b.counter.value

    def test_reset_sets_zero(self):
        self.b.counter.put_value(1234)
        assert 1234 == self.b.counter.value
        self.b.zero()
        assert 0 == self.b.counter.value
Esempio n. 11
0
class TestRawMotorPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        child = self.create_child_block(raw_motor_block,
                                        self.process,
                                        mri="mri",
                                        prefix="PV:PRE",
                                        motorPrefix="MOT:PRE",
                                        scannable="scan")
        child.parts["maxVelocity"].attr.set_value(5.0)
        child.parts["accelerationTime"].attr.set_value(0.5)
        child.parts["readback"].attr.set_value(12.3)
        child.parts["offset"].attr.set_value(4.5)
        child.parts["resolution"].attr.set_value(0.001)
        child.parts["csPort"].attr.set_value("CS1")
        child.parts["csAxis"].attr.set_value("Y")
        self.o = call_with_params(RawMotorPart, name="part", mri="mri")
        list(self.o.create_attribute_models())
        self.process.start()

    def tearDown(self):
        del self.context
        self.process.stop(timeout=1)

    def test_report(self):
        returns = self.o.report_cs_info(self.context)[0]
        assert returns.cs_axis == "Y"
        assert returns.cs_port == "CS1"
        assert returns.acceleration == 10.0
        assert returns.resolution == 0.001
        assert returns.offset == 4.5
        assert returns.max_velocity == 5.0
        assert returns.current_position == 12.3
        assert returns.scannable == "scan"
Esempio n. 12
0
class TestHelloPart(unittest.TestCase):
    def setUp(self):
        self.p = Process("proc")
        c = BasicController("mri")
        c.add_part(HelloPart(name='block'))
        self.p.add_controller(c)
        self.p.start()

    def tearDown(self):
        self.p.stop()

    def test_say_hello(self):
        b = self.p.block_view("mri")
        expected = "Hello test_name"
        response = b.greet("test_name", 0)
        assert expected == response

    def test_method_meta(self):
        b = self.p.block_view("mri")
        method = b.greet
        assert list(method.to_dict()) == [
            'typeid', 'takes', 'defaults', 'description', 'tags', 'writeable',
            'label', 'returns'
        ]
        assert method.defaults == dict(sleep=0.0)
        assert list(method.takes["elements"]) == ["name", "sleep"]
        assert list(method.returns["elements"]) == ["return"]
        assert method.tags == ["method:return:unpacked"]
        assert method.writeable
Esempio n. 13
0
class TestCounterDemoSystem(unittest.TestCase):
    def setUp(self):
        self.process = Process("proc")
        parts = [call_with_params(CounterPart, name="cpart")]
        self.controller = Controller(self.process, "counting", parts)
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_counter_subscribe(self):
        q = Queue()
        sub = Subscribe(id=20,
                        path=["counting", "counter"],
                        delta=False,
                        callback=q.put)
        self.controller.handle_request(sub)
        response = q.get(timeout=1.0)
        self.assertIsInstance(response, Update)
        assert response.id == 20
        assert response.value["typeid"] == "epics:nt/NTScalar:1.0"
        assert response.value["value"] == 0
        post = Post(id=21, path=["counting", "increment"], callback=q.put)
        self.controller.handle_request(post)
        response = q.get(timeout=1)
        self.assertIsInstance(response, Update)
        assert response.id == 20
        assert response.value["value"] == 1
        response = q.get(timeout=1)
        self.assertIsInstance(response, Return)
        assert response.id == 21
        assert response.value == None
        with self.assertRaises(TimeoutError):
            q.get(timeout=0.05)
class TestRunnableControllerCollectsAllParams(unittest.TestCase):
    def setUp(self):
        self.p = Process('process1')
        self.context = Context(self.p)

    def tearDown(self):
        self.p.stop(timeout=1)

    def test_no_hook_passes(self):
        # create a root block for the RunnableController block to reside in
        self.c = call_with_params(
            RunnableController,
            self.p, [PartTester1("1"), PartTester2("2")],
            mri='mainBlock',
            configDir="/tmp",
            axesToMove=["x"])
        self.p.add_controller('mainBlock', self.c)
        self.b = self.context.block_view("mainBlock")

        # start the process off
        self.p.start()

        takes = list(self.b.configure.takes.elements)
        self.assertEqual(takes, ["size", "generator", "axesToMove"])

    def test_hook_fails(self):
        # create a root block for the RunnableController block to reside in
        self.c = call_with_params(
            RunnableController,
            self.p, [PartTester1("1"), PartTester3("2")],
            mri='mainBlock',
            configDir="/tmp",
            axesToMove=["x"])
        self.p.add_controller('mainBlock', self.c)
        self.b = self.context.block_view("mainBlock")

        # start the process off
        self.p.start()

        takes = list(self.b.configure.takes.elements)
        self.assertEqual(takes, ["size", "generator", "axesToMove"])

    def test_hook_plus_method_takes_nothing_passes(self):
        # create a root block for the RunnableController block to reside in
        self.c = call_with_params(
            RunnableController,
            self.p, [PartTester1("1"), PartTester4("2")],
            mri='mainBlock',
            configDir="/tmp",
            axesToMove=["x"])
        self.p.add_controller('mainBlock', self.c)
        self.b = self.context.block_view("mainBlock")

        # start the process off
        self.p.start()

        takes = list(self.b.configure.takes.elements)
        self.assertEqual(takes, ["size", "generator", "axesToMove"])
Esempio n. 15
0
class TestProcess(unittest.TestCase):
    def setUp(self):
        self.o = Process("proc")
        self.o.start()

    def tearDown(self):
        self.o.stop(timeout=1)

    def test_init(self):
        assert self.o.name == "proc"

    def test_add_controller(self):
        controller = MagicMock(mri="mri")
        self.o.add_controller(controller)
        assert self.o.get_controller("mri") == controller

    def test_init_controller(self):
        class InitController(Controller):
            init = False

            def on_hook(self, hook):
                if isinstance(hook, ProcessStartHook):
                    self.init = True

        c = InitController("mri")
        self.o.add_controller(c)
        assert c.init == True

    def test_publish_controller(self):
        class PublishController(Controller):
            published = []

            def on_hook(self, hook):
                if isinstance(hook, ProcessPublishHook):
                    hook(self.do_publish)

            @add_call_types
            def do_publish(self, published):
                # type: (APublished) -> None
                self.published = published

        class UnpublishableController(Controller):
            def on_hook(self, hook):
                if isinstance(hook, ProcessStartHook):
                    hook(self.on_start)

            def on_start(self):
                return UnpublishedInfo(self.mri)

        c = PublishController("mri")
        self.o.add_controller(c)
        assert c.published == ["mri"]
        self.o.add_controller(Controller(mri="mri2"))
        assert c.published == ["mri", "mri2"]
        self.o.add_controller(UnpublishableController("mri3"))
        assert c.published == ["mri", "mri2"]
Esempio n. 16
0
class TestCounterDemoSystem(unittest.TestCase):
    def setUp(self):
        self.process = Process("proc")
        self.controller = Controller("counting")
        self.controller.add_part(CounterPart("cpart"))
        self.process.add_controller(self.controller)
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_counter_subscribe(self):
        q = Queue()
        # Subscribe to the value
        sub = Subscribe(id=20, path=["counting", "counter"], delta=False)
        sub.set_callback(q.put)
        self.controller.handle_request(sub)
        # Check initial return
        response = q.get(timeout=1.0)
        self.assertIsInstance(response, Update)
        assert response.id == 20
        assert response.value["typeid"] == "epics:nt/NTScalar:1.0"
        assert response.value["value"] == 0
        # Post increment()
        post = Post(id=21, path=["counting", "increment"])
        post.set_callback(q.put)
        self.controller.handle_request(post)
        # Check the value updates...
        response = q.get(timeout=1)
        self.assertIsInstance(response, Update)
        assert response.id == 20
        assert response.value["value"] == 1
        # ... then we get the return
        response = q.get(timeout=1)
        self.assertIsInstance(response, Return)
        assert response.id == 21
        assert response.value is None
        # Check we can put too
        put = Put(id=22, path=["counting", "counter", "value"], value=31)
        put.set_callback(q.put)
        self.controller.handle_request(put)
        # Check the value updates...
        response = q.get(timeout=1)
        self.assertIsInstance(response, Update)
        assert response.id == 20
        assert response.value["value"] == 31
        # ... then we get the return
        response = q.get(timeout=1)
        self.assertIsInstance(response, Return)
        assert response.id == 22
        assert response.value is None
        # And that there isn't anything else
        with self.assertRaises(TimeoutError):
            q.get(timeout=0.05)
Esempio n. 17
0
class TestIocIconPart(unittest.TestCase):
    def add_part_and_start(self):
        self.icon = IocIconPart(
            "ICON",
            os.path.split(__file__)[0] +
            "/../../../malcolm/modules/system/icons/epics-logo.svg",
        )
        self.c1.add_part(self.icon)
        self.p.add_controller(self.c1)
        self.p.start()

    def setUp(self):
        self.p = Process("process1")
        self.context = Context(self.p)
        self.c1 = RunnableController(mri="SYS",
                                     config_dir="/tmp",
                                     use_git=False)

    def tearDown(self):
        self.p.stop(timeout=1)

    @patch("malcolm.modules.ca.util.CAAttribute")
    def test_has_pv(self, CAAttribute):
        self.add_part_and_start()
        CAAttribute.assert_called_once_with(
            ANY,
            catools.DBR_STRING,
            "",
            "ICON:KERNEL_VERS",
            throw=False,
            callback=self.icon.update_icon,
        )
        assert isinstance(CAAttribute.call_args[0][0], StringMeta)
        meta = CAAttribute.call_args[0][0]
        assert meta.description == "Host Architecture"
        assert not meta.writeable
        assert len(meta.tags) == 0

    def test_adds_correct_icons(self):
        self.add_part_and_start()
        assert self.context.block_view("SYS").icon.value == defaultIcon

        arch = MockPv("windows")
        self.icon.update_icon(arch)
        assert self.context.block_view("SYS").icon.value == winIcon

        arch = MockPv("WIND")
        self.icon.update_icon(arch)
        assert self.context.block_view("SYS").icon.value == vxIcon

        arch = MockPv("linux")
        self.icon.update_icon(arch)
        assert self.context.block_view("SYS").icon.value == linuxIcon
Esempio n. 18
0
class TestOdinDWriterPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(odin_writer_block,
                                             self.process,
                                             mri="mri",
                                             prefix="prefix")
        self.o = OdinWriterPart(name="m", mri="mri")
        self.process.start()

        self.completed_steps = 0
        self.steps_to_do = 2000 * 3000
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3000, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2000)
        self.generator = CompoundGenerator([ys, xs], [], [], 0.1)
        self.generator.prepare()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_configure(self):
        self.o.configure(self.context,
                         self.completed_steps,
                         self.steps_to_do, {},
                         generator=self.generator,
                         fileDir='/tmp',
                         fileName='odin.hdf')
        assert self.child.handled_requests.mock_calls == [
            call.put('fileName', 'odin.hdf'),
            call.put('filePath', '/tmp/'),
            call.put('numCapture', self.steps_to_do),
            call.post('start')
        ]
        print(self.child.handled_requests.mock_calls)

    def test_run(self):
        self.o.configure(self.context,
                         self.completed_steps,
                         self.steps_to_do, {},
                         generator=self.generator,
                         fileDir='/tmp',
                         fileName='odin.hdf')
        self.child.handled_requests.reset_mock()
        self.o.registrar = MagicMock()
        # run waits for this value
        self.child.field_registry.get_field("numCaptured").set_value(
            self.o.done_when_reaches)
        self.o.run(self.context)
        assert self.child.handled_requests.mock_calls == []
        assert self.o.registrar.report.called_once
        assert self.o.registrar.report.call_args_list[0][0][0].steps == \
               self.steps_to_do
class TestReframePluginPart(ChildTestCase):

    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(
            reframe_plugin_block, self.process,
            mri="mri", prefix="prefix")
        self.o = ReframePluginPart(name="m", mri="mri")
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=2)

    def test_report(self):
        infos = self.o.report_status()
        assert infos.rank == 2

    def test_validate(self):
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 0.0002)
        generator.prepare()
        self.o.validate(generator)

    def test_validate_fails(self):
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 0.00009)
        generator.prepare()
        with self.assertRaises(AssertionError):
            self.o.validate(generator)

    def test_configure(self):
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2)
        generator = CompoundGenerator([ys, xs], [], [], 0.1)
        generator.prepare()
        completed_steps = 0
        steps_to_do = 6
        # We wait to be armed, so set this here
        self.set_attributes(self.child, acquiring=True)
        self.o.configure(
            self.context, completed_steps, steps_to_do, {}, generator)
        assert self.child.handled_requests.mock_calls == [
            call.put('arrayCallbacks', True),
            call.put('arrayCounter', 0),
            call.put('imageMode', 'Multiple'),
            call.put('numImages', 6),
            call.put('postCount', 999),
            call.post('start')]
Esempio n. 20
0
class TestSystemWSCommsServerAndClient(unittest.TestCase):
    socket = 8883

    def setUp(self):
        self.process = Process("proc")
        for controller in (hello_block(mri="hello") +
                           counter_block(mri="counter") +
                           web_server_block(mri="server", port=self.socket)):
            self.process.add_controller(controller)
        self.process.start()
        self.process2 = Process("proc2")
        for controller in (
                websocket_client_block(mri="client", port=self.socket) +
                proxy_block(mri="hello", comms="client") +
                proxy_block(mri="counter", comms="client")):
            self.process2.add_controller(controller)
        self.process2.start()

    def tearDown(self):
        self.socket += 1
        self.process.stop(timeout=1)
        self.process2.stop(timeout=1)

    def test_server_hello_with_malcolm_client(self):
        block2 = self.process2.block_view("hello")
        ret = block2.greet("me2")
        assert ret == "Hello me2"
        with self.assertRaises(ResponseError):
            block2.error()

    def test_server_counter_with_malcolm_client(self):
        block1 = self.process.block_view("counter")
        block2 = self.process2.block_view("counter")
        assert block2.counter.value == 0
        block2.increment()
        assert block2.counter.timeStamp.to_time(
        ) == block1.counter.timeStamp.to_time()
        assert block2.counter.value == 1
        block2.zero()
        assert block2.counter.value == 0
        assert self.process2.block_view("client").remoteBlocks.value.mri == [
            "hello",
            "counter",
            "server",
        ]

    def test_server_blocks(self):
        block = self.process.block_view("server")
        assert block.blocks.value.mri == ["hello", "counter", "server"]
        assert block.blocks.value.label == ["hello", "counter", "server"]
        assert block.blocks.meta.writeable is False
Esempio n. 21
0
class TestBlockModel(unittest.TestCase):
    @patch("malcolm.gui.guimodel.GuiModel.response_received")
    def setUp(self, mock_received):
        # Mock out the signal as it doesn't work without a QApplication running
        def register(func):
            self.saved_handle_response = func

        mock_received.connect.side_effect = register

        def emit(response):
            self.saved_handle_response(response)

        mock_received.emit.side_effect = emit

        self.process = Process("proc")
        self.controller = call_with_params(hello_block,
                                           self.process,
                                           mri="hello_block")
        self.process.start()
        self.m = GuiModel(self.process, self.controller.block_view())

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_init(self):
        assert self.m.root_item.endpoint == ('hello_block', )
        assert len(self.m.root_item.children) == 3

    def test_find_item(self):
        m1, m2 = MagicMock(), MagicMock()
        BlockItem.items[("foo", "bar")] = m1
        BlockItem.items[("foo", )] = m2
        item, path = self.m.find_item(('foo', 'bar', 'bat'))
        assert item == m1
        assert path == ['bat']

    def test_update_root(self):
        b_item = self.m.root_item
        assert [x.endpoint[-1]
                for x in b_item.children] == (["health", "error", "greet"])
        m_item = b_item.children[2]
        assert m_item.endpoint == ('hello_block', 'greet')
        assert len(m_item.children) == 2
        n_item = m_item.children[0]
        assert n_item.endpoint == (('hello_block', 'greet', 'takes',
                                    'elements', 'name'))
        assert n_item.children == []
        n_item = m_item.children[1]
        assert n_item.endpoint == (('hello_block', 'greet', 'takes',
                                    'elements', 'sleep'))
        assert n_item.children == []
class TestStatefulController(unittest.TestCase):
    def setUp(self):
        self.process = Process("proc")
        self.params = Mock()
        self.params.mri = "MyMRI"
        self.params.description = "My description"
        self.part = MyPart("testpart")
        self.o = StatefulController(self.process, [self.part], self.params)
        self.process.add_controller(self.params.mri, self.o)

    def start_process(self):
        self.process.start()
        self.addCleanup(self.stop_process)

    def stop_process(self):
        if self.process.started:
            self.process.stop(timeout=1)

    def test_process_init(self, ):
        assert not hasattr(self.part, "started")
        self.start_process()
        assert self.part.started

    def test_process_stop(self):
        self.start_process()
        assert not hasattr(self.part, "halted")
        self.process.stop(timeout=1)
        assert self.part.halted

    def test_init(self):
        assert self.o.state.value == "Disabled"
        self.start_process()
        assert self.o.state.value == "Ready"

    def test_reset_fails_from_ready(self):
        self.start_process()
        with self.assertRaises(TypeError):
            self.o.reset()
        assert not hasattr(self.part, "reset_done")

    def test_disable(self):
        self.start_process()
        assert not hasattr(self.part, "disable_done")
        self.o.disable()
        assert self.part.disable_done
        assert self.o.state.value == "Disabled"
        assert not hasattr(self.part, "reset_done")
        self.o.reset()
        assert self.part.reset_done
        assert self.o.state.value == "Ready"
Esempio n. 23
0
class TestCSPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(
            cs_block, self.process, mri="PMAC:CS1",
            prefix="PV:PRE")
        self.set_attributes(self.child, port="CS1")
        self.o = CSPart(name="pmac", mri="PMAC:CS1")
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_report_status(self):
        info = self.o.report_status(self.context)
        assert info.mri == "PMAC:CS1"
        assert info.port == "CS1"
Esempio n. 24
0
class TestAndorDetectorDriverPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(andor_detector_driver_block,
                                             self.process,
                                             mri="mri",
                                             prefix="prefix")
        choices = ["Fixed", "Continuous"]
        self.child.parts["imageMode"].attr.meta.set_choices(choices)
        self.o = call_with_params(AndorDriverPart,
                                  readoutTime=0.002,
                                  name="m",
                                  mri="mri")
        list(self.o.create_attribute_models())
        self.process.start()

    def tearDown(self):
        del self.context
        self.process.stop(timeout=1)

    def test_configure(self):
        params = MagicMock()
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3000, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2000)
        params.generator = CompoundGenerator([ys, xs], [], [], 0.1)
        params.generator.prepare()
        completed_steps = 0
        steps_to_do = 2000 * 3000
        part_info = ANY
        # configure looks at this value
        self.child.parts["exposure"].attr.set_value(0.098)
        self.o.configure(self.context, completed_steps, steps_to_do, part_info,
                         params)
        # Need to wait for the spawned mock start call to run
        self.o.start_future.result()
        assert self.child.handled_requests.mock_calls == [
            call.put('arrayCallbacks', True),
            call.put('arrayCounter', 0),
            call.put('numImages', 6000000),
            call.put('exposure', 0.098),
            call.put('acquirePeriod', 0.1),
            call.post('start')
        ]
Esempio n. 25
0
class TestEigerDetectorDriverPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(
            eiger_driver_block,
            self.process,
            mri="mri",
            prefix="prefix",
            fan_prefix="FAN",
        )
        self.o = EigerDriverPart(name="m", mri="mri")
        self.context.set_notify_dispatch_request(self.o.notify_dispatch_request)
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_configure(self):
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3000, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2000)
        generator = CompoundGenerator([ys, xs], [], [], 0.1)
        generator.prepare()
        completed_steps = 0
        steps_to_do = 2000 * 3000
        # We wait to be armed, so set this here
        self.set_attributes(self.child, acquiring=True, fanStateReady=1)
        self.o.on_configure(
            self.context,
            completed_steps,
            steps_to_do,
            {},
            generator=generator,
            fileDir="/tmp",
        )

        assert self.child.handled_requests.mock_calls == [
            call.put("arrayCallbacks", True),
            call.put("arrayCounter", 0),
            call.put("imageMode", "Multiple"),
            call.put("numImages", 6000000),
            call.put("numImagesPerSeries", 1),
            call.post("start"),
        ]
Esempio n. 26
0
class TestAndorDetectorDriverPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(andor_driver_block,
                                             self.process,
                                             mri="mri",
                                             prefix="prefix")
        # readoutTime used to be 0.002, not any more...
        self.o = AndorDriverPart(name="m", mri="mri")
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_configure(self):
        xs = LineGenerator("x", "mm", 0.0, 0.5, 3000, alternate=True)
        ys = LineGenerator("y", "mm", 0.0, 0.1, 2000)
        generator = CompoundGenerator([ys, xs], [], [], 0.1)
        generator.prepare()
        completed_steps = 0
        steps_to_do = 2000 * 3000
        # We wait to be armed, so set this here
        self.set_attributes(self.child, acquiring=True)
        # This is what the detector does when exposure and acquirePeriod are
        # both set to 0.1
        self.set_attributes(self.child, exposure=0.1, acquirePeriod=0.105)
        self.o.configure(self.context,
                         completed_steps,
                         steps_to_do, {},
                         generator=generator)
        assert self.child.handled_requests.mock_calls == [
            call.put('exposure', 0.1),
            call.put('acquirePeriod', 0.1),
            call.put('arrayCallbacks', True),
            call.put('arrayCounter', 0),
            # duration - readout - fudge_factor - crystal offset
            call.put('exposure', pytest.approx(0.1 - 0.005 - 0.0014 - 5e-6)),
            call.put('imageMode', 'Multiple'),
            call.put('numImages', 6000000),
            call.put('acquirePeriod', 0.1 - 5e-6),
            call.post('start')
        ]
Esempio n. 27
0
class TestXmap3DetectorDriverPart(ChildTestCase):
    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(xmap_driver_block,
                                             self.process,
                                             mri="mri",
                                             prefix="prefix")
        self.o = XmapDriverPart(name="m", mri="mri")
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    def test_configure(self):
        completed_steps = 0
        steps_to_do = 456
        self.o.post_configure = MagicMock()
        # We wait to be armed, so set this here
        self.set_attributes(self.child, acquiring=True)
        self.o.configure(self.context, completed_steps, steps_to_do, {},
                         MagicMock())
        # Wait for the start_future so the post gets through to our child
        # even on non-cothread systems
        self.o.actions.start_future.result(timeout=1)
        assert self.child.handled_requests.mock_calls == [
            call.put('arrayCallbacks', True),
            call.put('arrayCounter', completed_steps),
            call.put('autoPixelsPerBuffer', 'Manual'),
            call.put('binsInSpectrum', 2048),
            call.put('collectMode', 'MCA mapping'),
            call.put('dxp1MaxEnergy', 4.096),
            call.put('dxp2MaxEnergy', 4.096),
            call.put('dxp3MaxEnergy', 4.096),
            call.put('dxp4MaxEnergy', 4.096),
            call.put('ignoreGate', 'No'),
            call.put('inputLogicPolarity', 'Normal'),
            call.put('pixelAdvanceMode', 'Gate'),
            call.put('pixelsPerBuffer', 1),
            call.put('pixelsPerRun', steps_to_do),
            call.put('presetMode', 'No preset'),
            call.post('start')
        ]
Esempio n. 28
0
class TestXmap3DetectorDriverPart(ChildTestCase):

    def setUp(self):
        self.process = Process("Process")
        self.context = Context(self.process)
        self.child = self.create_child_block(
            xmap_detector_driver_block, self.process,
            mri="mri", prefix="prefix")
        self.o = call_with_params(XmapDriverPart, name="m", mri="mri")
        list(self.o.create_attribute_models())
        self.process.start()

    def tearDown(self):
        del self.context
        self.process.stop(timeout=1)

    def test_configure(self):
        params = MagicMock()
        completed_steps = 1234
        steps_to_do = 456
        part_info = ANY
        self.o.post_configure = MagicMock()
        self.o.configure(
            self.context, completed_steps, steps_to_do, part_info, params)
        # Need to wait for the spawned mock start call to run
        self.o.start_future.result()
        assert self.child.handled_requests.mock_calls == [
            call.put('arrayCallbacks', True),
            call.put('arrayCounter', completed_steps),
            call.put('autoPixelsPerBuffer', 'Manual'),
            call.put('binsInSpectrum', 2048),
            call.put('collectMode', 'MCA mapping'),
            call.put('dxp1MaxEnergy', 4.096),
            call.put('dxp2MaxEnergy', 4.096),
            call.put('dxp3MaxEnergy', 4.096),
            call.put('dxp4MaxEnergy', 4.096),
            call.put('ignoreGate', 'No'),
            call.put('inputLogicPolarity', 'Normal'),
            call.put('pixelAdvanceMode', 'Gate'),
            call.put('pixelsPerBuffer', 1),
            call.put('pixelsPerRun', steps_to_do),
            call.put('presetMode', 'No preset'),
            call.post('start')]
Esempio n. 29
0
class TestSystemWSCommsServerAndClient(unittest.TestCase):
    socket = 8883

    def setUp(self):
        self.process = Process("proc")
        self.hello = call_with_params(hello_block, self.process, mri="hello")
        self.counter = call_with_params(
            counter_block, self.process, mri="counter")
        self.server = call_with_params(
            web_server_block, self.process, mri="server", port=self.socket)
        self.process.start()
        self.process2 = Process("proc2")
        self.client = call_with_params(
            websocket_client_block, self.process2, mri="client",
            port=self.socket)
        self.process2.start()

    def tearDown(self):
        self.socket += 1
        self.process.stop(timeout=1)
        self.process2.stop(timeout=1)

    def test_server_hello_with_malcolm_client(self):
        call_with_params(
            proxy_block, self.process2, mri="hello", comms="client")
        block2 = self.process2.block_view("hello")
        ret = block2.greet("me2")
        assert ret == dict(greeting="Hello me2")
        with self.assertRaises(ResponseError):
            block2.error()

    def test_server_counter_with_malcolm_client(self):
        call_with_params(
            proxy_block, self.process2, mri="counter", comms="client")
        block2 = self.process2.block_view("counter")
        assert block2.counter.value == 0
        block2.increment()
        assert block2.counter.value == 1
        block2.zero()
        assert block2.counter.value == 0
        assert self.client.remote_blocks.value == (
            "hello", "counter", "server")
Esempio n. 30
0
class TestSystemRest(unittest.TestCase):
    socket = 8886

    def setUp(self):
        self.process = Process("proc")
        self.hello = hello_block(mri="hello")[-1]
        self.process.add_controller(self.hello)
        self.server = web_server_block(mri="server", port=self.socket)[-1]
        self.process.add_controller(self.server)
        self.result = Queue()
        self.http_client = AsyncHTTPClient()
        self.process.start()

    def tearDown(self):
        self.process.stop(timeout=1)

    @gen.coroutine
    def get(self, mri):
        result = yield self.http_client.fetch("http://localhost:%s/rest/%s" %
                                              (self.socket, mri))
        cothread.Callback(self.result.put, result)

    @gen.coroutine
    def post(self, mri, method, args):
        req = HTTPRequest(
            "http://localhost:%s/rest/%s/%s" % (self.socket, mri, method),
            method="POST",
            body=args,
        )
        result = yield self.http_client.fetch(req)
        cothread.Callback(self.result.put, result)

    def test_get_hello(self):
        IOLoopHelper.call(self.get, "hello")
        result = self.result.get(timeout=2)
        assert result.body.decode().strip() == json_encode(self.hello._block)

    def test_post_hello(self):
        IOLoopHelper.call(self.post, "hello", "greet",
                          json_encode(dict(name="me")))
        result = self.result.get(timeout=2)
        assert result.body.decode().strip() == json_encode("Hello me")
class TestSystemWSCommsServerAndClient(unittest.TestCase):
    socket = 8882

    def setUp(self):
        sf = SyncFactory("sync")
        self.process = Process("proc", sf)
        Hello(self.process, dict(mri="hello"))
        Counter(self.process, dict(mri="counter"))
        self.process.add_comms(
            WebsocketServerComms(self.process, dict(port=self.socket)))
        self.process.start()
        # If we don't wait long enough, sometimes the websocket_connect()
        # in process2 will hang...
        time.sleep(0.1)
        self.process2 = Process("proc2", sf)
        self.process2.add_comms(
            WebsocketClientComms(self.process2,
                             dict(hostname="localhost", port=self.socket)))
        self.process2.start()

    def tearDown(self):
        self.socket += 1
        self.process.stop()
        self.process2.stop()

    def test_server_hello_with_malcolm_client(self):
        block2 = self.process2.make_client_block("hello")
        task = Task("task", self.process2)
        futures = task.when_matches_async(block2["state"], "Ready")
        task.wait_all(futures, timeout=1)
        ret = block2.greet("me2")
        self.assertEqual(ret, dict(greeting="Hello me2"))

    def test_server_counter_with_malcolm_client(self):
        block2 = self.process2.make_client_block("counter")
        task = Task("task", self.process2)
        futures = task.when_matches_async(block2["state"], "Ready")
        task.wait_all(futures, timeout=1)
        self.assertEqual(block2.counter, 0)
        block2.increment()
        self.assertEqual(block2.counter, 1)
class TestSystemWSCommsServerOnly(unittest.TestCase):
    socket = 8881

    def setUp(self):
        self.process = Process("proc", SyncFactory("sync"))
        Hello(self.process, dict(mri="hello"))
        self.process.add_comms(
            WebsocketServerComms(self.process, dict(port=self.socket)))
        self.process.start()

    def tearDown(self):
        self.process.stop()

    @gen.coroutine
    def send_message(self):
        conn = yield websocket_connect("ws://localhost:%s/ws" % self.socket)
        req = dict(
            type="malcolm:core/Post:1.0",
            id=0,
            endpoint=["hello", "greet"],
            parameters=dict(
                name="me"
            )
        )
        conn.write_message(json.dumps(req))
        resp = yield conn.read_message()
        resp = json.loads(resp)
        self.assertEqual(resp, dict(
            id=0,
            type="malcolm:core/Return:1.0",
            value=dict(
                greeting="Hello me"
            )
        ))
        conn.close()

    def test_server_and_simple_client(self):
        self.send_message()
Esempio n. 33
0
class TestChildPart(unittest.TestCase):

    def checkState(self, state):
        self.assertEqual(self.c.state.value, state)

    def makeChildBlock(self, blockMri):
        params = PortsPart.MethodMeta.prepare_input_map(name='Connector')
        port_part = PortsPart(self.p, params)

        partName = 'part%s' % blockMri
        params = DefaultController.MethodMeta.prepare_input_map(mri=blockMri)
        controller = DefaultController(self.p, [port_part], params)

        params = ChildPart.MethodMeta.prepare_input_map(
            mri=blockMri, name=partName)
        part = ChildPart(self.p, params)

        return part, controller

    def setUp(self):
        self.p = Process('process1', SyncFactory('threading'))

        self.p1, self.c1 = self.makeChildBlock('child1')
        self.p2, self.c2 = self.makeChildBlock('child2')
        self.p3, self.c3 = self.makeChildBlock('child3')

        # create a root block for the child blocks to reside in
        parts = [self.p1, self.p2, self.p3]
        params = RunnableController.MethodMeta.prepare_input_map(
            mri='mainBlock')
        self.c = RunnableController(self.p, parts, params)
        self.b = self.c.block

        params = ChildPart.MethodMeta.prepare_input_map(
            mri='mainBlock', name='mainPart')
        self.part = ChildPart(self.p, params)

        # Get the parent block into idle state
        self.p.start()

        # wait until block is Ready
        task = Task("block_ready_task", self.p)
        task.when_matches(self.c.block["state"], sm.IDLE, timeout=1)

        self.checkState(sm.IDLE)

    def tearDown(self):
        self.p.stop()

    def test_init(self):
        # check instantiation of object tree via logger names
        self.assertEqual(self.c._logger.name,
                         'RunnableController(mainBlock)')
        self.assertEqual(self.c.parts['partchild1']._logger.name,
                         'RunnableController(mainBlock).partchild1')
        self.assertEqual(self.c.parts['partchild2']._logger.name,
                         'RunnableController(mainBlock).partchild2')
        self.assertEqual(self.c.parts['partchild3']._logger.name,
                         'RunnableController(mainBlock).partchild3')

        self.assertEqual(self.c1.parts['Connector']._logger.name,
                         'DefaultController(child1).Connector')
        self.assertEqual(self.c2.parts['Connector']._logger.name,
                         'DefaultController(child2).Connector')
        self.assertEqual(self.c3.parts['Connector']._logger.name,
                         'DefaultController(child3).Connector')

        self.assertEqual(self.c1.block.inportConnector, '')
        self.assertEqual(self.c1.block.outportConnector, '')

    def test_reset(self):
        # TODO cover the clause 'state == RESETTING'
        self.c.disable()
        self.checkState(sm.DISABLED)
        self.c.reset()
        self.checkState(sm.IDLE)

    def test_pre_layout(self):
        outports = self.p1.pre_layout(None)
        self.assertEqual(len(outports), 1)

    def test_layout(self):
        self.c.edit()
        self.checkState(sm.EDITABLE)

        new_layout = Table(self.c.layout.meta)
        new_layout.name = ["partchild1", "partchild2", "partchild3"]
        new_layout.mri = ["part1", "part2", "part3"]
        new_layout.x = [10, 11, 12]
        new_layout.y = [20, 21, 22]
        new_layout.visible = [True, True, True]
        self.b.layout = new_layout
        self.assertEqual(self.c.parts['partchild1'].x, 10)
        self.assertEqual(self.c.parts['partchild1'].y, 20)
        self.assertEqual(self.c.parts['partchild1'].visible, True)
        self.assertEqual(self.c.parts['partchild2'].x, 11)
        self.assertEqual(self.c.parts['partchild2'].y, 21)
        self.assertEqual(self.c.parts['partchild2'].visible, True)
        self.assertEqual(self.c.parts['partchild3'].x, 12)
        self.assertEqual(self.c.parts['partchild3'].y, 22)
        self.assertEqual(self.c.parts['partchild3'].visible, True)

        new_layout.visible = [True, False, True]
        self.b.layout= new_layout
        self.assertEqual(self.c.parts['partchild1'].visible, True)
        self.assertEqual(self.c.parts['partchild2'].visible, False)
        self.assertEqual(self.c.parts['partchild3'].visible, True)

    def test_sever_all_inports(self):
        self.c1.block.inportConnector = 'Connector'
        self.c2.block.inportConnector = 'Connector'
        self.c3.block.inportConnector = 'Connector3'

        task = Task("Task1" , self.p)
        self.p1.sever_all_inports(task)
        task.wait_all([],5)
        self.assertEqual(self.c1.block.inportConnector, '')
        self.assertEqual(self.c2.block.inportConnector, 'Connector')
        self.assertEqual(self.c3.block.inportConnector, 'Connector3')

    def test_sever_inports_connected_to(self):
        self.c1.block.inportConnector = 'Connector'

        self.assertEqual(self.c1.block.inportConnector, 'Connector')

        task = Task("Task1" , self.p)
        out_port = {'Connector': 'pos'}
        self.p1.sever_inports_connected_to(task, out_port)
        self.assertEqual(self.c1.block.inportConnector, '')

    def test_get_flowgraph_ports(self):
        count = len(self.p1._get_flowgraph_ports('out'))
        self.assertEqual(count, 1)
        count = len(self.p1._get_flowgraph_ports('in'))
        self.assertEqual(count, 1)
class TestRunnableController(unittest.TestCase):

    def checkState(self, state, child=True, parent=True):
        if child:
            self.assertEqual(self.b_child.state, state)
        if parent:
            self.assertEqual(self.b.state, state)

    def checkSteps(self, configured, completed, total):
        self.assertEqual(self.b.configuredSteps, configured)
        self.assertEqual(self.b.completedSteps, completed)
        self.assertEqual(self.b.totalSteps, total)
        self.assertEqual(self.b_child.configuredSteps, configured)
        self.assertEqual(self.b_child.completedSteps, completed)
        self.assertEqual(self.b_child.totalSteps, total)

    def setUp(self):
        self.maxDiff = 5000

        self.p = Process('process1', SyncFactory('threading'))

        # Make a ticker block to act as our child
        params = Ticker.MethodMeta.prepare_input_map(mri="childBlock")
        self.b_child = Ticker(self.p, params)[-1]

        # Make an empty part for our parent
        params = Part.MethodMeta.prepare_input_map(name='part1')
        part1 = Part(self.p, params)

        # Make a RunnableChildPart to control the ticker
        params = RunnableChildPart.MethodMeta.prepare_input_map(
            mri='childBlock', name='part2')
        part2 = RunnableChildPart(self.p, params)

        # create a root block for the RunnableController block to reside in
        params = RunnableController.MethodMeta.prepare_input_map(
            mri='mainBlock')
        self.c = RunnableController(self.p, [part1, part2], params)
        self.b = self.c.block
        self.sm = self.c.stateMachine

        # start the process off
        self.p.start()

        # wait until block is Ready
        task = Task("block_ready_task", self.p)
        task.when_matches(self.b["state"], self.sm.IDLE, timeout=1)

        self.checkState(self.sm.IDLE)

    def tearDown(self):
        self.p.stop()

    def test_init(self):
        # the following block attributes should be created by a call to
        # set_attributes via _set_block_children in __init__
        self.assertEqual(self.b['totalSteps'].meta.typeid,
                         'malcolm:core/NumberMeta:1.0')
        self.assertEqual(self.b['layout'].meta.typeid,
                         'malcolm:core/TableMeta:1.0')
        self.assertEqual(self.b['completedSteps'].meta.typeid,
                         'malcolm:core/NumberMeta:1.0')
        self.assertEqual(self.b['configuredSteps'].meta.typeid,
                         'malcolm:core/NumberMeta:1.0')
        self.assertEqual(self.b['axesToMove'].meta.typeid,
                         'malcolm:core/StringArrayMeta:1.0')
        self.assertEqual(self.b['layoutName'].meta.typeid,
                         'malcolm:core/StringMeta:1.0')

        # the following hooks should be created via _find_hooks in __init__
        self.assertEqual(self.c.hook_names, {
            self.c.Reset: "Reset",
            self.c.Disable: "Disable",
            self.c.ReportOutports: "ReportOutports",
            self.c.Layout: "Layout",
            self.c.Load: "Load",
            self.c.Save: "Save",
            self.c.Validate: "Validate",
            self.c.ReportStatus: "ReportStatus",
            self.c.Configure: "Configure",
            self.c.PostConfigure: "PostConfigure",
            self.c.Run: "Run",
            self.c.PostRunReady: "PostRunReady",
            self.c.PostRunIdle: "PostRunIdle",
            self.c.Seek: "Seek",
            self.c.Pause: "Pause",
            self.c.Resume: "Resume",
            self.c.Abort: "Abort",
        })

        # check instantiation of object tree via logger names
        self.assertEqual(self.c._logger.name,
                         'RunnableController(mainBlock)')
        self.assertEqual(self.c.parts['part1']._logger.name,
                         'RunnableController(mainBlock).part1')
        self.assertEqual(self.c.parts['part2']._logger.name,
                         'RunnableController(mainBlock).part2')

    def test_edit(self):
        self.b.edit()
        self.checkState(self.sm.EDITABLE, child=False)

    def test_reset(self):
        self.b.disable()
        self.checkState(self.sm.DISABLED)
        self.b.reset()
        self.checkState(self.sm.IDLE)

    def test_set_axes_to_move(self):
        self.c.set_axes_to_move(['y'])
        self.assertEqual(self.c.axes_to_move.value, ['y'])

    def test_validate(self):
        line1 = LineGenerator('y', 'mm', 0, 2, 3)
        line2 = LineGenerator('x', 'mm', 0, 2, 2)
        compound = CompoundGenerator([line1, line2], [], [])
        actual = self.b.validate(generator=compound, axesToMove=['x'])
        self.assertEqual(actual["generator"], compound)
        self.assertEqual(actual["axesToMove"], ['x'])

    def prepare_half_run(self, duration=0.01, exception=0):
        line1 = LineGenerator('y', 'mm', 0, 2, 3)
        line2 = LineGenerator('x', 'mm', 0, 2, 2)
        duration = FixedDurationMutator(duration)
        compound = CompoundGenerator([line1, line2], [], [duration])
        self.b.configure(
            generator=compound, axesToMove=['x'], exceptionStep=exception)

    def test_configure_run(self):
        self.prepare_half_run()
        self.checkSteps(2, 0, 6)
        self.checkState(self.sm.READY)

        self.b.run()
        self.checkState(self.sm.READY)
        self.checkSteps(4, 2, 6)

        self.b.run()
        self.checkState(self.sm.READY)
        self.checkSteps(6, 4, 6)

        self.b.run()
        self.checkState(self.sm.IDLE)

    def test_abort(self):
        self.prepare_half_run()
        self.b.run()
        self.b.abort()
        self.checkState(self.sm.ABORTED)

    def test_pause_seek_resume(self):
        self.prepare_half_run()
        self.checkSteps(configured=2, completed=0, total=6)
        self.b.run()
        self.checkState(self.sm.READY)
        self.checkSteps(4, 2, 6)
        self.b.pause(completedSteps=1)
        self.checkState(self.sm.READY)
        self.checkSteps(2, 1, 6)
        self.b.run()
        self.checkSteps(4, 2, 6)
        self.b.completedSteps = 5
        self.checkSteps(6, 5, 6)
        self.b.run()
        self.checkState(self.sm.IDLE)

    def test_resume_in_run(self):
        self.prepare_half_run(duration=0.5)
        w = self.p.spawn(self.b.run)
        time.sleep(0.85)
        self.b.pause()
        self.checkState(self.sm.PAUSED)
        self.checkSteps(2, 1, 6)
        self.b.resume()
        # return to PRERUN should continue original run to completion and
        # READY state
        then = time.time()
        w.wait()
        self.assertAlmostEqual(time.time() - then, 0.5, delta=0.4)
        self.checkState(self.sm.READY)

    def test_run_exception(self):
        self.prepare_half_run(exception=1)
        with self.assertRaises(ResponseError):
            self.b.run()
        self.checkState(self.sm.FAULT)

    def test_run_stop(self):
        self.prepare_half_run(duration=0.5)
        w = self.p.spawn(self.b.run)
        time.sleep(0.5)
        self.b.abort()
        with self.assertRaises(AbortedError):
            w.get()
        self.checkState(self.sm.ABORTED)
class TestManagerController(unittest.TestCase):
    def checkState(self, state, child=True, parent=True):
        if child:
            self.assertEqual(self.c_child.state.value, state)
        if parent:
            self.assertEqual(self.c.state.value, state)

    def setUp(self):
        self.p = Process("process1", SyncFactory("threading"))

        # create a child ManagerController block
        params = ManagerController.MethodMeta.prepare_input_map(mri="childBlock")
        self.c_child = ManagerController(self.p, [], params)
        self.b_child = self.c_child.block

        self.sm = self.c_child.stateMachine

        params = Part.MethodMeta.prepare_input_map(name="part1")
        part1 = Part(self.p, params)
        params = {"name": "part2", "mri": "childBlock"}
        params = ChildPart.MethodMeta.prepare_input_map(**params)
        part2 = ChildPart(self.p, params)

        # create a root block for the ManagerController block to reside in
        parts = [part1, part2]
        params = {"mri": "mainBlock"}
        params = ManagerController.MethodMeta.prepare_input_map(**params)
        self.c = ManagerController(self.p, parts, params)
        self.b = self.c.block

        # check that do_initial_reset works asynchronously
        self.p.start()

        # wait until block is Ready
        task = Task("block_ready_task", self.p)
        task.when_matches(self.b["state"], self.sm.READY, timeout=1)

        self.checkState(self.sm.READY)

    def tearDown(self):
        self.p.stop()

    def test_init(self):

        # the following block attributes should be created by a call to
        # set_attributes via _set_block_children in __init__
        self.assertEqual(self.b["layout"].meta.typeid, "malcolm:core/TableMeta:1.0")
        self.assertEqual(self.b["layoutName"].meta.typeid, "malcolm:core/StringMeta:1.0")

        # the following hooks should be created via _find_hooks in __init__
        self.assertEqual(
            self.c.hook_names,
            {
                self.c.Reset: "Reset",
                self.c.Disable: "Disable",
                self.c.Layout: "Layout",
                self.c.ReportOutports: "ReportOutports",
                self.c.Load: "Load",
                self.c.Save: "Save",
            },
        )

        # check instantiation of object tree via logger names
        self.assertEqual(self.c._logger.name, "ManagerController(mainBlock)")
        self.assertEqual(self.c.parts["part1"]._logger.name, "ManagerController(mainBlock).part1")
        self.assertEqual(self.c.parts["part2"]._logger.name, "ManagerController(mainBlock).part2")
        self.assertEqual(self.c_child._logger.name, "ManagerController(childBlock)")

    def test_edit(self):
        self.c.edit()
        # editing only affects one level
        self.checkState(self.sm.EDITABLE, child=False)
        self.assertEqual(self.c.revert_structure, self.c._save_to_structure())

    def test_edit_exception(self):
        self.c.edit()
        with self.assertRaises(Exception):
            self.c.edit()

    def test_save(self):
        self.c.edit()
        params = {"layoutName": "testSaveLayout"}
        params = ManagerController.save.MethodMeta.prepare_input_map(**params)
        self.c.save(params)
        self.checkState(self.sm.AFTER_RESETTING, child=False)
        self.assertEqual(self.c.layout_name.value, "testSaveLayout")
        self.c.edit()
        params = {"layoutName": None}
        params = ManagerController.save.MethodMeta.prepare_input_map(**params)
        self.c.save(params)

    def test_revert(self):
        self.c.edit()
        self.c.revert()
        self.checkState(self.sm.AFTER_RESETTING, child=False)

    def test_set_and_load_layout(self):
        self.c.edit()
        self.checkState(self.sm.EDITABLE, child=False)

        new_layout = Table(self.c.layout.meta)
        new_layout.name = ["part2"]
        new_layout.mri = ["P45-MRI"]
        new_layout.x = [10]
        new_layout.y = [20]
        new_layout.visible = [True]
        self.b.layout = new_layout
        self.assertEqual(self.c.parts["part2"].x, 10)
        self.assertEqual(self.c.parts["part2"].y, 20)
        self.assertEqual(self.c.parts["part2"].visible, True)

        # save the layout, modify and restore it
        params = {"layoutName": "testSaveLayout"}
        params = ManagerController.save.MethodMeta.prepare_input_map(**params)
        self.c.save(params)

        self.c.edit()
        new_layout.x = [30]
        self.b.layout = new_layout
        self.assertEqual(self.c.parts["part2"].x, 30)
        self.b.layoutName = "testSaveLayout"
        self.assertEqual(self.c.parts["part2"].x, 10)