def do_test_ack(self, text):
        # Create a channel ------------------------------------------
        # its buffer will be used for both serializing # the instance
        # data and, by deserializing it, for creating a second instance.
        chan = Channel(BUFSIZE)
        buf = chan.buffer
        self.assertEqual(BUFSIZE, len(buf))

        # create the ack_msg_cls class ------------------------------
        ack_spec = self.s_obj_model.msgs[0]
        msg_name = ack_spec.name
        self.assertEqual('ack', msg_name)

        ack_msg_cls = make_msg_class(self.s_obj_model, 'ack')

        # create a message instance ---------------------------------
        values = [text]
        ack = ack_msg_cls(values)
        (_) = tuple(values)             # WAS text1

        # pylint: disable=no-member
        self.assertEqual(ack_spec.name, ack.name)
        # we don't have any nested enums or messages
        # pylint: disable=no-member
        self.assertEqual(0, len(ack.enums))
        # pylint: disable=no-member
        self.assertEqual(0, len(ack.msgs))

        # pylint: disable=no-member
        self.assertEqual(1, len(ack.fieldClasses))
        self.assertEqual(1, len(ack))        # number of fields in instance
        for ndx, value in enumerate(ack):
            self.assertEqual(values[ndx], value)

        # verify fields are accessible in the object ----------------
        # pylint: disable=no-member
        self.assertEqual(text, ack.text)

        # serialize the object to the channel -----------------------
        buf = chan.buffer
        chan.clear()
        nnn = ack.write_stand_alone(chan)
        self.assertEqual(0, nnn)                         # returns msg index
        chan.flip()

        print("ACTUAL LENGTH OF SERIALIZED ACK OBJECT: %u" % chan.limit)

        # deserialize the channel, making a clone of the message ----
        (readback, _) = MsgImpl.read(chan, self.s_obj_model)
        self.assertIsNotNone(readback)
        self.assertTrue(ack.__eq__(readback))

        # produce another message from the same values --------------
        ack2 = ack_msg_cls(values)
        chan2 = Channel(BUFSIZE)
        nnn = ack2.write_stand_alone(chan2)
        chan2.flip()
        (copy2, nn3) = ack_msg_cls.read(chan2, self.s_obj_model)
        self.assertTrue(ack.__eq__(readback))
        self.assertTrue(ack2.__eq__(copy2))
        self.assertEqual(nnn, nn3)                # GEEP
    def test_le_msg_serialization(self):

        # parse the protoSpec

        # XXX highly questionable:
        #   verify that this adds 1 (msg) + 5 (field count) to the number
        #   of entries in getters, putters, etc

        self.assertIsNotNone(self.s_obj_model)
        self.assertTrue(isinstance(self.s_obj_model, M.ProtoSpec))
        self.assertEqual('org.xlattice.ringd', self.s_obj_model.name)

        self.assertEqual(0, len(self.s_obj_model.enums))
        self.assertEqual(11, len(self.s_obj_model.msgs))
        self.assertEqual(0, len(self.s_obj_model.seqs))

        # XXX a foolish test, but we use the variable 'leMsgSpec' below
        le_msg_spec = self.s_obj_model.msgs[5]
        msg_name = le_msg_spec.name
        self.assertEqual('logEntry', msg_name)

        # Create a channel ------------------------------------------
        # its buffer will be used for both serializing # the instance
        # data and, by deserializing it, for creating a second instance.
        chan = Channel(BUFSIZE)
        buf = chan.buffer
        self.assertEqual(BUFSIZE, len(buf))

        # create the LogEntryMsg class ------------------------------
        log_entry_msg = make_msg_class(self.s_obj_model, 'logEntry')

        # create a message instance ---------------------------------
        values = self.le_msg_values()        # a list of quasi-random values
        le_msg = log_entry_msg(values)
        (timestamp, key, length, node_id, src, path) = tuple(values)

        # DEBUG
        print("TIMESTAMP = %d" % timestamp)
        print("KEY       = %s" % key)
        print("LENGTH    = %s" % length)
        # END

        # pylint: disable=no-member
        self.assertEqual(le_msg_spec.name, le_msg.name)
        # we don't have any nested enums or messages
        self.assertEqual(0, len(le_msg.enums))
        self.assertEqual(0, len(le_msg.msgs))

        # pylint: disable=no-member
        self.assertEqual(6, len(le_msg.fieldClasses))
        self.assertEqual(6, len(le_msg))        # number of fields in instance
        for ndx, value in enumerate(le_msg):
            self.assertEqual(value, values[ndx])

        # verify fields are accessible in the object ----------------
        #(timestamp, key, length, nodeID, src, path) = tuple(values)
        self.assertEqual(timestamp, le_msg.timestamp)
        # pylint: disable=no-member
        self.assertEqual(key, le_msg.key)
        # pylint: disable=no-member
        self.assertEqual(length, le_msg.length)
        # pylint: disable=no-member
        self.assertEqual(node_id, le_msg.node_id)
        # pylint: disable=no-member
        self.assertEqual(src, le_msg.src)
        # pylint: disable=no-member
        self.assertEqual(path, le_msg.path)

        # serialize the object to the channel -----------------------
        buf = chan.buffer
        chan.clear()
        nnn = le_msg.write_stand_alone(chan)
        self.assertEqual(5, nnn)                         # returns msg index
        old_position = chan.position                     # TESTING flip()
        chan.flip()
        self.assertEqual(old_position, chan.limit)      # TESTING flip()
        self.assertEqual(0, chan.position)   # TESTING flip()
        actual = chan.limit

        print("ACTUAL LENGTH OF SERIALIZED OBJECT: %u" % actual)

        # deserialize the channel, making a clone of the message ----
        (readback, _) = MsgImpl.read(chan, self.s_obj_model)
        self.assertIsNotNone(readback)
        self.assertTrue(le_msg.__eq__(readback))

        # produce another message from the same values --------------
        le_msg2 = log_entry_msg(values)
        chan2 = Channel(BUFSIZE)
        nnn = le_msg2.write_stand_alone(chan2)
        chan2.flip()
        (copy2, nn3) = log_entry_msg.read(chan2, self.s_obj_model)
        self.assertTrue(le_msg.__eq__(readback))
        self.assertTrue(le_msg2.__eq__(copy2))
        self.assertEqual(nnn, nn3)                # GEEP
    def test_shutdown_msg(self):
        # DEBUG
        print("\nTEST_SHUTDOWN_MSG")
        # END

        # -----------------------------------------------------------
        # XXX This code has been crudely hacked from another test
        # module, and so needs careful review
        # -----------------------------------------------------------

        # verify that this adds 1 (msg) + 3 (field count) to the number
        # of entries in getters, writeStandAlones, etc

        msg_spec = self.str_obj_model.msgs[2]          # <------
        msg_name = msg_spec.name
        self.assertEqual('shutdown', msg_name)

        # Create a channel ------------------------------------------
        # its buffer will be used for both serializing # the instance
        # data and, by deserializing it, for creating a second instance.
        chan = Channel(BUFSIZE)
        buf = chan.buffer
        self.assertEqual(BUFSIZE, len(buf))

        # create the CorruptListMsg class ------------------------------
        shutdown_msg_cls = make_msg_class(self.str_obj_model, msg_name)

        # create a message instance ---------------------------------
        values = [RNG.next_file_name(8), ]  # list of quasi-random values
        sd_msg = shutdown_msg_cls(values)

        self.assertEqual(msg_name, sd_msg._name)
        # we don't have any nested enums or messages
        self.assertEqual(0, len(sd_msg.enums))
        self.assertEqual(0, len(sd_msg.msgs))

        self.assertEqual(1, len(sd_msg.field_classes))   # <---
        self.assertEqual(1, len(sd_msg))        # number of fields in instance
        self.assertEqual(values[0], sd_msg.remarks)

        # serialize the object to the channel -----------------------
        sd_msg.write_stand_alone(chan)
        chan.flip()

        # deserialize the channel, making a clone of the message ----
        (read_back, nn5) = MsgImpl.read(chan, self.str_obj_model)
        self.assertIsNotNone(read_back)

        # DEBUG
        print("position after shutdown read back is %d" % chan.position)
        # END

        # verify that the messages are identical --------------------
        self.assertTrue(sd_msg.__eq__(read_back))

        # produce another message from the same values --------------
        sd_msg2 = shutdown_msg_cls(values)
        chan2 = Channel(BUFSIZE)
        sd_msg2.write_stand_alone(chan2)
        chan2.flip()

        (copy2, nn6) = MsgImpl.read(chan2, self.str_obj_model)
        self.assertTrue(sd_msg.__eq__(copy2))
        self.assertTrue(sd_msg2.__eq__(copy2))       # GEEP GEEP GEEP
    def test_zone_mismatch_msg(self):
        # DEBUG
        print("\nTEST_ZONE_MISMATCH_MSG")
        # END

        # from setUp(): =============================================

        # end stuff from setup ======================================

        # -----------------------------------------------------------
        # XXX This code has been crudely hacked from another test
        # module, and so needs careful review
        # -----------------------------------------------------------

        # verify that this adds 1 (msg) + 3 (field count) to the number
        # of entries in getters, putters, etc

        self.assertIsNotNone(self.str_obj_model)
        self.assertTrue(isinstance(self.str_obj_model, M.ProtoSpec))
        self.assertEqual('org.xlattice.alertz', self.str_obj_model.name)

        self.assertEqual(0, len(self.str_obj_model.enums))
        self.assertEqual(16, len(self.str_obj_model.msgs))
        self.assertEqual(0, len(self.str_obj_model.seqs))

        msg_spec = self.str_obj_model.msgs[0]
        msg_name = msg_spec.name
        self.assertEqual('zoneMismatch', msg_name)

        # Create a channel ------------------------------------------
        # its buffer will be used for both serializing # the instance
        # data and, by deserializing it, for creating a second instance.
        chan = Channel(BUFSIZE)
        buf = chan.buffer
        self.assertEqual(BUFSIZE, len(buf))

        # create the ZoneMismatchMsg class ------------------------------
        zone_mismatch_msg_cls = make_msg_class(self.str_obj_model, msg_name)

        # create a message instance ---------------------------------
        values = self.zone_mismatch_fields()        # list of quasi-random values
        zmm_msg = zone_mismatch_msg_cls(values)

        self.assertEqual(msg_spec.name, zmm_msg._name)
        # we don't have any nested enums or messages
        self.assertEqual(0, len(zmm_msg.enums))
        self.assertEqual(0, len(zmm_msg.msgs))

        self.assertEqual(5, len(zmm_msg.field_classes))
        self.assertEqual(5, len(zmm_msg))        # number of fields in instance
        self.assertEqual(values[0], zmm_msg.timestamp)
        self.assertEqual(values[1], zmm_msg.seq_nbr)
        self.assertEqual(values[2], zmm_msg.zone_name)
        self.assertEqual(values[3], zmm_msg.expected_serial)
        self.assertEqual(values[4], zmm_msg.actual_serial)

        # serialize the object to the channel -----------------------

        # XXX WRITE HEADER FIRST!

        # DEBUG
        print("DIR (ZMM_MSG): ", end=' ')
        print(dir(zmm_msg))
        # END
        zmm_msg.write_stand_alone(chan)
        chan.flip()

        # deserialize the channel, making a clone of the message ----
        (read_back, nn2) = MsgImpl.read(chan, self.str_obj_model)
        self.assertIsNotNone(read_back)

        # DEBUG
        print("position after mis-match read back is %d" % chan.position)
        # END

        # verify that the messages are identical --------------------
        self.assertTrue(zmm_msg.__eq__(read_back))

        # produce another message from the same values --------------
        zmm_msg2 = zone_mismatch_msg_cls(values)
        chan2 = Channel(BUFSIZE)
        zmm_msg2.write_stand_alone(chan2)
        chan2.flip()

        (copy2, nn3) = MsgImpl.read(chan2, self.str_obj_model)
        self.assertTrue(zmm_msg.__eq__(copy2))
        self.assertTrue(zmm_msg2.__eq__(copy2))       # GEEP
    def test_the_daemon(self):
        chan = Channel(BUFSIZE)
        chan.clear()        # XXX should be guaranteed on new channel

        msg_count = 8 + RNG.next_int16(25)   # so 8..32
        # DEBUG
        print("MSG_COUNT = %u" % msg_count)
        # END

        # set up options ----------------------------------
        now = int(time.time())
        pgm_name_and_version = "testWithDummyClient v%s %s" % (
            __version__, __version_date__)
        with open('/etc/hostname', 'r') as file:
            this_host = file.read().strip()

        options = {}                           # a namespace, so to speak
        options['ec2Host'] = False
        options['justShow'] = False
        options['log_dir'] = 'logs'
        options['pgm_name_and_version'] = pgm_name_and_version
        options['port'] = 55555
        options['showTimestamp'] = False
        options['showVersion'] = False
        options['testing'] = True
        options['this_host'] = this_host
        options['timestamp'] = now
        options['verbose'] = False
        ns_ = Namespace(options)

        # clear the log files (so delete any files under logs/) -----
        self.do_clear_logs(ns_)

        # start the daemon --------------------------------
        daemon_t = threading.Thread(target=run_the_daemon, args=(ns_,))
        daemon_t.start()

        # give the daemon time to wake up  --------------------------
        time.sleep(0.15)    # XXX without this we get an abort saying
        # that libev cannot allocate (2G - 16)B

        # start sending (some fixed number of ) messages ------------
        msgs_sent = []
        for nnn in range(msg_count):
            msg = self.next_zone_mismatch_msg()
            seq_nbr_field = msg[1]
            # XXX by name would be better!
            self.assertEqual(nnn, seq_nbr_field.value)

            # serialize msg into the channel
            chan.clear()
            msg.write_stand_alone(chan)
            chan.flip()

            # send the msg to the daemon ------------------
            skt = send_to_end_point(chan, '127.0.0.1', 55555)
            time.sleep(0.05)
            skt.close()
            msgs_sent.append(msg)

            # DEBUG
            print("MSG %d HAS BEEN SENT" % nnn)
            # END

        self.assertEqual(msg_count, len(msgs_sent))

        # delay a few ms --------------------------------------------
        time.sleep(0.05)

        # build and send shutdown msg -------------------------------
        msg = self.next_shutdown_msg()
        chan.clear()
        msg.write_stand_alone(chan)
        chan.flip()

        skt = send_to_end_point(chan, '127.0.0.1', 55555)
        # DEBUG
        print("SHUTDOWN MSG HAS BEEN SENT")
        # END

        # delay a few ms --------------------------------------------
        time.sleep(0.05)
        skt.close()

        # join the daemon thread ------------------------------------
        time.sleep(0.05)
        daemon_t.join()