class ZmqSystemTest(unittest.TestCase):
    # class ZmqSystemTest(object):

    def setUp(self):
        # Spawn ds under cothread
        self.ds = DirectoryService(["zmq://ipc:///tmp/sock.ipc"])
        self.ds.run(block=False)
        self.lp = Process(
            [], "Local Process", ds_string="zmq://ipc:///tmp/sock.ipc")
        self.lp.run(block=False)
        self.lp.ds.createCounter(name="The Counter")
        self.c = self.lp.get_device("The Counter")

    def test_simple_function(self):
        import cothread
        cothread.Sleep(0.2)
        start = self.c.getCount()
        self.assertEqual(self.c.hello(), "world")
        # Hello world takes about 10 ticks
        self.assertAlmostEqual(self.c.getCount(), start + 10, delta=3)
        # Do a long running call
        s = cothread.Spawn(self.c.longHello)
        # Check it returns immediately
        self.assertAlmostEqual(self.c.getCount(), start + 10, delta=3)
        self.assertEqual(self.c.hello(), "world")
        # Hello world takes 10 ticks
        self.assertAlmostEqual(self.c.getCount(), start + 20, delta=3)
        self.assertEqual(s.Wait(), "long world")
        # Long hello takes about 50 ticks from send
        self.assertAlmostEqual(self.c.getCount(), start + 60, delta=8)
        s.Wait()

    def tearDown(self):
        self.c = None
        self.lp.ds.exit()
        self.lp.exit()
        self.lp = None
        self.ds.loop_wait()
        self.ds = None
class ProcessTest(unittest.TestCase):

    def setUp(self):
        self.p = Process(["tst://socket"], "The Process")
        self.p.run(block=False)
        self.s = self.p._server_socks["tst://socket"]

    def test_gc(self):
        msgs = []

        def log_debug(msg):
            msgs.append(msg)
        self.p.log_debug = log_debug
        self.s = None
        self.p = None
        self.assertEqual(msgs, ['Garbage collecting loop', 'Stopping loop', 'Waiting for loop to finish', 
                                'Confirming loop stopped', 'Loop garbage collected'])

    def test_create_device(self):
        self.assertEqual(self.p.device_types, [
                         'Device', 'RunnableDevice', 'PausableDevice', 'DummyDet', 'DeviceClient', 'Process', 'MockDevice'])
        d = self.p.create_MockDevice("MD")
        self.assertIsInstance(d, MockDevice)
        self.assertEqual(d.name, "MD")

    def test_call_on_device(self):
        d = self.p.create_MockDevice("MD")
        self.s.inq.Signal((SType.Call, dict(endpoint="MD", method="run")))
        self.assertEqual(d.runcalled, False)
        # Yield to let socket recv
        cothread.Yield()
        # Yield to let process retrieve from inq
        cothread.Yield()
        # Yield to let spawned do_func run
        cothread.Yield()
        self.assertEqual(d.runcalled, True)
        self.assertIsNot(self.s.send, None)
        self.s.send.assert_called_once_with(SType.Return, 32)

    def test_get_on_device(self):
        d = self.p.create_MockDevice("MD")
        self.s.inq.Signal((SType.Get, dict(endpoint="MD.methods")))
        # Yield to let socket recv
        cothread.Yield()
        # Yield to let process retrieve from inq
        cothread.Yield()
        self.assertIsNot(self.s.send, None)
        self.s.send.assert_called_once_with(SType.Return, d.methods)

    def test_subscribe_on_device(self):
        d = self.p.create_MockDevice("MD")
        self.s.inq.Signal((SType.Subscribe, dict(endpoint="MD.methods.run")))
        # Yield to let socket recv
        cothread.Yield()
        # Yield to let process retrieve from inq
        cothread.Yield()
        d.notify_listeners({"methods.run.descriptor": "new desc"})
        # Yield to let subscription send back out
        cothread.Yield()
        self.assertIsNot(self.s.send, None)
        self.s.send.assert_called_once_with(
            SType.Value, d.methods["run"], changes=dict(descriptor="new desc"))
        self.s.send.reset_mock()
        self.s.inq.Signal((SType.Unsubscribe, {}))
        # Yield to let socket recv
        cothread.Yield()
        # Yield to let process retrieve from inq
        cothread.Yield()
        self.s.send.assert_called_once_with(
            SType.Return)
        # Yield to let subscription remove itself from parent
        cothread.Yield()