Пример #1
0
    def test_simulator_startup(self):
        # start the server
        server = TCPSimulatorServer()
        self.addCleanup(server.close)

        self.assertGreater(server.port, 0)
        log.debug("Simulator up")

        # connect to the server
        client = TCPSimulatorClient(server.port)
        self.addCleanup(client.close)

        # Now send some data and see if we can read it.
        result = ""
        orig_data = "some data"
        server.send(orig_data)
        for i in range(0, 10):
            bytes_read = client.read()
            if (bytes_read != None):
                result += bytes_read

            if (result != orig_data):
                time.sleep(1)

        self.assertEqual(result, orig_data)
    def test_simulator_startup(self):
        # start the server
        server = TCPSimulatorServer()
        self.addCleanup(server.close)

        self.assertGreater(server.port, 0)
        log.debug("Simulator up")

        # connect to the server
        client = TCPSimulatorClient(server.port)
        self.addCleanup(client.close)

        # Now send some data and see if we can read it.
        result = ""
        orig_data = "some data"
        server.send(orig_data)
        for i in range(0, 10):
            bytes_read = client.read()
            if(bytes_read != None):
                result += bytes_read

            if(result != orig_data):
                time.sleep(1)

        self.assertEqual(result, orig_data)
    def init_instrument_simulator(self):
        """
        Startup a TCP server that we can use as an instrument simulator
        """
        self._instrument_simulator = TCPSimulatorServer()
        self.addCleanup(self._instrument_simulator.close)

        # Wait for the simulator to bind to a port
        timeout = time.time() + 10
        while timeout > time.time():
            if self._instrument_simulator.port > 0:
                log.debug("Instrument simulator initialized on port %s" % self._instrument_simulator.port)
                return

            log.debug("waiting for simulator to bind. sleeping")
            time.sleep(1)

        raise IDKException("Timeout waiting for simulator to bind")
    def init_instrument_simulator(self):
        """
        Startup a TCP server that we can use as an instrument simulator
        """
        self._instrument_simulator = TCPSimulatorServer()
        self.addCleanup(self._instrument_simulator.close)

        # Wait for the simulator to bind to a port
        timeout = time.time() + 10
        while timeout > time.time():
            if self._instrument_simulator.port > 0:
                log.debug("Instrument simulator initialized on port %s" % self._instrument_simulator.port)
                return

            log.debug("waiting for simulator to bind. sleeping")
            time.sleep(1)

        raise IDKException("Timeout waiting for simulator to bind")
class PAClientIntTestCase(InstrumentDriverTestCase):

    def setUp(self):
        # InstrumentDriverIntegrationTestCase.setUp(self)

        self.ipaddr = "localhost"
        self.cmd_port = 9001
        self.data_port = 9002
        self.device_port = 9003

        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
        self.pa_packet = None

    def tearDown(self):
        """
        @brief Test teardown
        """
        log.debug("PACClientIntTestCase tearDown")

        InstrumentDriverTestCase.tearDown(self)

    def startPortAgent(self):
        pa_port = self.init_port_agent()
        log.debug("port_agent started on port: %d" % pa_port)
        time.sleep(2)  # give it a chance to start responding

    def resetTestVars(self):
        log.debug("Resetting test variables...")
        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
        self.listenerCallbackCalled = False

    def myGotData(self, pa_packet):
        self.dataCallbackCalled = True
        self.pa_packet = pa_packet
        if pa_packet.is_valid():
            validity = "valid"
        else:
            validity = "invalid"

        log.debug("Got %s port agent data packet with data length %s: %s", validity, pa_packet.get_data_length(),
                  pa_packet.get_data())

    def myGotRaw(self, pa_packet):
        self.rawCallbackCalled = True
        if pa_packet.is_valid():
            validity = "valid"
        else:
            validity = "invalid"

        log.debug("Got %s port agent raw packet with data length %s: %s", validity, pa_packet.get_data_length(),
                  pa_packet.get_data())

    def myGotListenerError(self, exception):
        self.listenerCallbackCalled = True
        log.info("Got listener exception: %s", exception)

    def myGotError(self, error_string="No error string passed in."):
        self.errorCallbackCalled = True
        log.info("myGotError got error: %s", error_string)

    def init_instrument_simulator(self):
        """
        Startup a TCP server that we can use as an instrument simulator
        """
        self._instrument_simulator = TCPSimulatorServer()
        self.addCleanup(self._instrument_simulator.close)

        # Wait for the simulator to bind to a port
        timeout = time.time() + 10
        while timeout > time.time():
            if self._instrument_simulator.port > 0:
                log.debug("Instrument simulator initialized on port %s" % self._instrument_simulator.port)
                return

            log.debug("waiting for simulator to bind. sleeping")
            time.sleep(1)

        raise IDKException("Timeout waiting for simulator to bind")

    def init_port_agent(self):
        """
        @brief Launch the driver process and driver client.  This is used in the
        integration and qualification tests.  The port agent abstracts the physical
        interface with the instrument.
        @retval return the pid to the logger process
        """
        if self.port_agent:
            log.error("Port agent already initialized")
            return

        log.debug("Startup Port Agent")

        # comm_config = self.get_comm_config()

        config = self.port_agent_config()
        log.debug("port agent config: %s" % config)

        port_agent = PortAgentProcess.launch_process(config, timeout=60, test_mode=True)

        port = port_agent.get_data_port()
        pid = port_agent.get_pid()

        log.info('Started port agent pid %s listening at port %s' % (pid, port))

        self.addCleanup(self.stop_port_agent)
        self.port_agent = port_agent
        return port

    def port_agent_config(self):
        """
        Overload the default port agent configuration so that
        it connects to a simulated TCP connection.
        """
        config = {'device_addr': 'localhost',
                  'device_port': self._instrument_simulator.port,
                  'command_port': self.cmd_port,
                  'data_port': self.data_port,
                  'process_type': PortAgentProcessType.UNIX,
                  'log_level': 5,
                  'heartbeat_interval': 3}

        # Override the instrument connection information.

        return config

    def test_pa_client_retry(self):
        """
        Test that the port agent client will not continually try to recover
        when the port agent closes the connection gracefully because it has
        another client connected.
        """

        exception_raised = False
        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()
        time.sleep(2)

        # Start a TCP client that will connect to the data port; this sets up the
        # situation where the Port Agent will immediately close the connection
        # because it already has one
        self.tcp_client = TcpClient("localhost", self.data_port)
        time.sleep(2)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)
        except InstrumentConnectionException:
            exception_raised = True

        # Give it some time to retry
        time.sleep(4)

        self.assertTrue(exception_raised)

    def test_pa_client_rx_heartbeat(self):
        """
        Test that the port agent can send heartbeats when the pa_client has
        a heartbeat_interval of 0.  The port_agent_config() method above
        sets the heartbeat interval.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()
        time.sleep(5)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)

        time.sleep(10)

        self.assertFalse(self.errorCallbackCalled)

    def test_start_pa_client_no_port_agent(self):

        self.resetTestVars()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        self.assertRaises(InstrumentConnectionException,
                          pa_client.init_comms,
                          self.myGotData, self.myGotRaw,
                          self.myGotListenerError, self.myGotError)

        self.assertFalse(self.errorCallbackCalled)

    def test_start_pa_client_with_port_agent(self):

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

            data = "this is a great big test"
            pa_client.send(data)

            time.sleep(1)

            self._instrument_simulator.send(data)

            time.sleep(5)

        pa_client.stop_comms()

        # Assert that the error_callback was not called, that an exception was not
        # caught, and that the data and raw callbacks were called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertFalse(exception_caught)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

    def test_start_pa_client_no_port_agent_big_data(self):

        self.resetTestVars()

        logging.getLogger('mi.core.instrument.port_agent_client').setLevel(logging.DEBUG)

        # I put this in here because PortAgentPacket cannot make a new packet
        # with a valid checksum.
        def makepacket(msgtype, timestamp, data):
            from struct import Struct

            SYNC = (0xA3, 0x9D, 0x7A)
            HEADER_FORMAT = "!BBBBHHd"
            header_struct = Struct(HEADER_FORMAT)
            HEADER_SIZE = header_struct.size

            def calculate_checksum(data, seed=0):
                n = seed
                for datum in data:
                    n ^= datum
                return n

            def pack_header(buf, msgtype, pktsize, checksum, timestamp):
                sync1, sync2, sync3 = SYNC
                header_struct.pack_into(buf, 0, sync1, sync2, sync3, msgtype, pktsize,
                                        checksum, timestamp)

            pktsize = HEADER_SIZE + len(data)
            pkt = bytearray(pktsize)
            pack_header(pkt, msgtype, pktsize, 0, timestamp)
            pkt[HEADER_SIZE:] = data
            checksum = calculate_checksum(pkt)
            pack_header(pkt, msgtype, pktsize, checksum, timestamp)
            return pkt

        # Make a BIG packet
        data = "A" * (2 ** 16 - HEADER_SIZE - 1)
        txpkt = makepacket(PortAgentPacket.DATA_FROM_INSTRUMENT, 0.0, data)

        def handle(sock, addr):
            # Send it in pieces
            sock.sendall(txpkt[:1500])
            time.sleep(1)
            sock.sendall(txpkt[1500:])
            time.sleep(10)

        import gevent.server

        dataserver = gevent.server.StreamServer((self.ipaddr, self.data_port), handle)
        cmdserver = gevent.server.StreamServer((self.ipaddr, self.cmd_port), lambda x, y: None)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            dataserver.start()
            cmdserver.start()
            pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            raise

        else:
            time.sleep(5)

        finally:
            pa_client.stop_comms()
            dataserver.kill()
            cmdserver.kill()

        # Assert that the error_callback was not called, that an exception was not
        # caught, and that the data and raw callbacks were called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

        self.assertEquals(self.pa_packet.get_data_length(), len(data))
        self.assertEquals(len(self.pa_packet.get_data()), len(data))
        # don't use assertEquals b/c it will print 64kb
        self.assert_(self.pa_packet.get_data() == data)

    def test_start_pa_client_lost_port_agent_tx_rx(self):
        """
        This test starts the port agent and the instrument_simulator and
        tests that data is sent and received first; then it stops the port
        agent and tests that the error_callback was called.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)

        # Now send some data; there should be no errors.
        try:
            data = "this is a great big test"
            pa_client.send(data)

            time.sleep(1)

            self._instrument_simulator.send(data)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

        time.sleep(1)

        # Assert that the error_callback was NOT called, that an exception was NOT
        # caught, and that the data and raw callbacks WERE called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertFalse(exception_caught)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

        # Now reset the test variables and try again; this time after stopping
        # the port agent.  Should be errors
        self.resetTestVars()

        try:
            self.stop_port_agent()
            log.debug("Port agent stopped")
            data = "this is another great big test"
            pa_client.send(data)

            time.sleep(1)

            log.debug("Sending from simulator")
            self._instrument_simulator.send(data)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)

        time.sleep(5)

        # Assert that the error_callback WAS called.  The listener usually
        # is seeing the error first, and that does not call the exception, so
        # only assert that the error callback was called.
        self.assertTrue(self.errorCallbackCalled)

    def test_start_pa_client_lost_port_agent_rx(self):
        """
        This test starts the port agent and then stops the port agent and
        verifies that the error callback was called (because the listener
        is the only one that will see the error, since there is no send
        operation).
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotListenerError, self.myGotError)

        try:
            self.stop_port_agent()

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)

        time.sleep(5)

        # Assert that the error_callback was called.  At this moment the listener
        # is seeing the error first, and that does not call the exception, so
        # don't test for that yet.
        self.assertTrue(self.errorCallbackCalled)

    @unittest.skip('Skip; this test does not work consistently.')
    def test_start_pa_client_lost_port_agent_tx(self):
        """
        This test starts the port agent and then starts the port agent client
        in a special way that will not start the listener thread.  This will
        guarantee that the send context is the one the sees the error.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        # Give the port agent time to initialize
        time.sleep(5)

        pa_client.init_comms(self.myGotData, self.myGotRaw, self.myGotError, self.myGotListenerError,
                             start_listener=False)

        try:
            self.stop_port_agent()
            data = "this big ol' test should cause send context to fail"
            pa_client.send(data)

            time.sleep(1)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

        time.sleep(5)

        # Assert that the error_callback was called.  For this test the listener
        # should not be running, so the send context should see the error, and that
        # should throw an exception.  Assert that the callback WAS called and that
        # an exception WAS thrown.
        self.assertTrue(self.errorCallbackCalled)
        self.assertTrue(exception_caught)
Пример #6
0
class PAClientIntTestCase(InstrumentDriverTestCase):
    def setUp(self):
        # InstrumentDriverIntegrationTestCase.setUp(self)

        self.ipaddr = "localhost"
        self.cmd_port = 9001
        self.data_port = 9002
        self.device_port = 9003

        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
        self.pa_packet = None

    def tearDown(self):
        """
        @brief Test teardown
        """
        log.debug("PACClientIntTestCase tearDown")

        InstrumentDriverTestCase.tearDown(self)

    def startPortAgent(self):
        pa_port = self.init_port_agent()
        log.debug("port_agent started on port: %d" % pa_port)
        time.sleep(2)  # give it a chance to start responding

    def resetTestVars(self):
        log.debug("Resetting test variables...")
        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
        self.listenerCallbackCalled = False

    def myGotData(self, pa_packet):
        self.dataCallbackCalled = True
        self.pa_packet = pa_packet
        if pa_packet.is_valid():
            validity = "valid"
        else:
            validity = "invalid"

        log.debug("Got %s port agent data packet with data length %s: %s",
                  validity, pa_packet.get_data_length(), pa_packet.get_data())

    def myGotRaw(self, pa_packet):
        self.rawCallbackCalled = True
        if pa_packet.is_valid():
            validity = "valid"
        else:
            validity = "invalid"

        log.debug("Got %s port agent raw packet with data length %s: %s",
                  validity, pa_packet.get_data_length(), pa_packet.get_data())

    def myGotListenerError(self, exception):
        self.listenerCallbackCalled = True
        log.info("Got listener exception: %s", exception)

    def myGotError(self, error_string="No error string passed in."):
        self.errorCallbackCalled = True
        log.info("myGotError got error: %s", error_string)

    def init_instrument_simulator(self):
        """
        Startup a TCP server that we can use as an instrument simulator
        """
        self._instrument_simulator = TCPSimulatorServer()
        self.addCleanup(self._instrument_simulator.close)

        # Wait for the simulator to bind to a port
        timeout = time.time() + 10
        while timeout > time.time():
            if self._instrument_simulator.port > 0:
                log.debug("Instrument simulator initialized on port %s" %
                          self._instrument_simulator.port)
                return

            log.debug("waiting for simulator to bind. sleeping")
            time.sleep(1)

        raise IDKException("Timeout waiting for simulator to bind")

    def init_port_agent(self):
        """
        @brief Launch the driver process and driver client.  This is used in the
        integration and qualification tests.  The port agent abstracts the physical
        interface with the instrument.
        @retval return the pid to the logger process
        """
        if self.port_agent:
            log.error("Port agent already initialized")
            return

        log.debug("Startup Port Agent")

        # comm_config = self.get_comm_config()

        config = self.port_agent_config()
        log.debug("port agent config: %s" % config)

        port_agent = PortAgentProcess.launch_process(config,
                                                     timeout=60,
                                                     test_mode=True)

        port = port_agent.get_data_port()
        pid = port_agent.get_pid()

        log.info('Started port agent pid %s listening at port %s' %
                 (pid, port))

        self.addCleanup(self.stop_port_agent)
        self.port_agent = port_agent
        return port

    def port_agent_config(self):
        """
        Overload the default port agent configuration so that
        it connects to a simulated TCP connection.
        """
        config = {
            'device_addr': 'localhost',
            'device_port': self._instrument_simulator.port,
            'command_port': self.cmd_port,
            'data_port': self.data_port,
            'process_type': PortAgentProcessType.UNIX,
            'log_level': 5,
            'heartbeat_interval': 3
        }

        # Override the instrument connection information.

        return config

    def test_pa_client_retry(self):
        """
        Test that the port agent client will not continually try to recover
        when the port agent closes the connection gracefully because it has
        another client connected.
        """

        exception_raised = False
        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()
        time.sleep(2)

        # Start a TCP client that will connect to the data port; this sets up the
        # situation where the Port Agent will immediately close the connection
        # because it already has one
        self.tcp_client = TcpClient("localhost", self.data_port)
        time.sleep(2)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            pa_client.init_comms(self.myGotData, self.myGotRaw,
                                 self.myGotListenerError, self.myGotError)
        except InstrumentConnectionException:
            exception_raised = True

        # Give it some time to retry
        time.sleep(4)

        self.assertTrue(exception_raised)

    def test_pa_client_rx_heartbeat(self):
        """
        Test that the port agent can send heartbeats when the pa_client has
        a heartbeat_interval of 0.  The port_agent_config() method above
        sets the heartbeat interval.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()
        time.sleep(5)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw,
                             self.myGotListenerError, self.myGotError)

        time.sleep(10)

        self.assertFalse(self.errorCallbackCalled)

    def test_start_pa_client_no_port_agent(self):

        self.resetTestVars()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        self.assertRaises(InstrumentConnectionException, pa_client.init_comms,
                          self.myGotData, self.myGotRaw,
                          self.myGotListenerError, self.myGotError)

        self.assertFalse(self.errorCallbackCalled)

    def test_start_pa_client_with_port_agent(self):

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            pa_client.init_comms(self.myGotData, self.myGotRaw,
                                 self.myGotListenerError, self.myGotError)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

            data = "this is a great big test"
            pa_client.send(data)

            time.sleep(1)

            self._instrument_simulator.send(data)

            time.sleep(5)

        pa_client.stop_comms()

        # Assert that the error_callback was not called, that an exception was not
        # caught, and that the data and raw callbacks were called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertFalse(exception_caught)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

    def test_start_pa_client_no_port_agent_big_data(self):

        self.resetTestVars()

        logging.getLogger('mi.core.instrument.port_agent_client').setLevel(
            logging.DEBUG)

        # I put this in here because PortAgentPacket cannot make a new packet
        # with a valid checksum.
        def makepacket(msgtype, timestamp, data):
            from struct import Struct

            SYNC = (0xA3, 0x9D, 0x7A)
            HEADER_FORMAT = "!BBBBHHd"
            header_struct = Struct(HEADER_FORMAT)
            HEADER_SIZE = header_struct.size

            def calculate_checksum(data, seed=0):
                n = seed
                for datum in data:
                    n ^= datum
                return n

            def pack_header(buf, msgtype, pktsize, checksum, timestamp):
                sync1, sync2, sync3 = SYNC
                header_struct.pack_into(buf, 0, sync1, sync2, sync3, msgtype,
                                        pktsize, checksum, timestamp)

            pktsize = HEADER_SIZE + len(data)
            pkt = bytearray(pktsize)
            pack_header(pkt, msgtype, pktsize, 0, timestamp)
            pkt[HEADER_SIZE:] = data
            checksum = calculate_checksum(pkt)
            pack_header(pkt, msgtype, pktsize, checksum, timestamp)
            return pkt

        # Make a BIG packet
        data = "A" * (2**16 - HEADER_SIZE - 1)
        txpkt = makepacket(PortAgentPacket.DATA_FROM_INSTRUMENT, 0.0, data)

        def handle(sock, addr):
            # Send it in pieces
            sock.sendall(txpkt[:1500])
            time.sleep(1)
            sock.sendall(txpkt[1500:])
            time.sleep(10)

        import gevent.server

        dataserver = gevent.server.StreamServer((self.ipaddr, self.data_port),
                                                handle)
        cmdserver = gevent.server.StreamServer((self.ipaddr, self.cmd_port),
                                               lambda x, y: None)

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        try:
            dataserver.start()
            cmdserver.start()
            pa_client.init_comms(self.myGotData, self.myGotRaw,
                                 self.myGotListenerError, self.myGotError)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            raise

        else:
            time.sleep(5)

        finally:
            pa_client.stop_comms()
            dataserver.kill()
            cmdserver.kill()

        # Assert that the error_callback was not called, that an exception was not
        # caught, and that the data and raw callbacks were called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

        self.assertEquals(self.pa_packet.get_data_length(), len(data))
        self.assertEquals(len(self.pa_packet.get_data()), len(data))
        # don't use assertEquals b/c it will print 64kb
        self.assert_(self.pa_packet.get_data() == data)

    def test_start_pa_client_lost_port_agent_tx_rx(self):
        """
        This test starts the port agent and the instrument_simulator and
        tests that data is sent and received first; then it stops the port
        agent and tests that the error_callback was called.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw,
                             self.myGotListenerError, self.myGotError)

        # Now send some data; there should be no errors.
        try:
            data = "this is a great big test"
            pa_client.send(data)

            time.sleep(1)

            self._instrument_simulator.send(data)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

        time.sleep(1)

        # Assert that the error_callback was NOT called, that an exception was NOT
        # caught, and that the data and raw callbacks WERE called.
        self.assertFalse(self.errorCallbackCalled)
        self.assertFalse(exception_caught)
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)

        # Now reset the test variables and try again; this time after stopping
        # the port agent.  Should be errors
        self.resetTestVars()

        try:
            self.stop_port_agent()
            log.debug("Port agent stopped")
            data = "this is another great big test"
            pa_client.send(data)

            time.sleep(1)

            log.debug("Sending from simulator")
            self._instrument_simulator.send(data)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)

        time.sleep(5)

        # Assert that the error_callback WAS called.  The listener usually
        # is seeing the error first, and that does not call the exception, so
        # only assert that the error callback was called.
        self.assertTrue(self.errorCallbackCalled)

    def test_start_pa_client_lost_port_agent_rx(self):
        """
        This test starts the port agent and then stops the port agent and
        verifies that the error callback was called (because the listener
        is the only one that will see the error, since there is no send
        operation).
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        pa_client.init_comms(self.myGotData, self.myGotRaw,
                             self.myGotListenerError, self.myGotError)

        try:
            self.stop_port_agent()

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)

        time.sleep(5)

        # Assert that the error_callback was called.  At this moment the listener
        # is seeing the error first, and that does not call the exception, so
        # don't test for that yet.
        self.assertTrue(self.errorCallbackCalled)

    @unittest.skip('Skip; this test does not work consistently.')
    def test_start_pa_client_lost_port_agent_tx(self):
        """
        This test starts the port agent and then starts the port agent client
        in a special way that will not start the listener thread.  This will
        guarantee that the send context is the one the sees the error.
        """

        self.resetTestVars()

        self.init_instrument_simulator()
        self.startPortAgent()

        pa_client = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)

        # Give the port agent time to initialize
        time.sleep(5)

        pa_client.init_comms(self.myGotData,
                             self.myGotRaw,
                             self.myGotError,
                             self.myGotListenerError,
                             start_listener=False)

        try:
            self.stop_port_agent()
            data = "this big ol' test should cause send context to fail"
            pa_client.send(data)

            time.sleep(1)

        except InstrumentConnectionException as e:
            log.error("Exception caught: %r" % e)
            exception_caught = True

        else:
            exception_caught = False

        time.sleep(5)

        # Assert that the error_callback was called.  For this test the listener
        # should not be running, so the send context should see the error, and that
        # should throw an exception.  Assert that the callback WAS called and that
        # an exception WAS thrown.
        self.assertTrue(self.errorCallbackCalled)
        self.assertTrue(exception_caught)
class PAClientIntTestCase(InstrumentDriverTestCase):
#class PAClientIntTestCase(MiIntTestCase):

    def initialize(cls, *args, **kwargs):
        print "initialize"
        
    def setUp(self):
        #InstrumentDriverIntegrationTestCase.setUp(self)

        """
        DHE: Change this to init my own simulator
        """
        #self.ipaddr = "69.196.56.192"
        self.ipaddr = "localhost"
        self.cmd_port = 9001
        self.data_port  = 9002
        self.device_port = 9003
        
        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
        
    def tearDown(self):
        """
        @brief Test teardown
        """
        log.debug("PACClientIntTestCase tearDown")

        InstrumentDriverTestCase.tearDown(self)

    def startPortAgent(self):
        pa_port = self.init_port_agent()
        print "port_agent started on port: " + str(pa_port)

    def resetTestVars(self):
        self.rawCallbackCalled = False
        self.dataCallbackCalled = False
        self.errorCallbackCalled = False
            
    def myGotData(self, paPacket):
        self.dataCallbackCalled = True
        if paPacket.is_valid():
            validity = "valid"
        else:
            validity = "invalid"
            
        print "Got " + validity + " port agent data packet with data length " + str(paPacket.get_data_size()) + ": " + str(paPacket.get_data())

    def myGotRaw(self, paPacket):
        self.rawCallbackCalled = True
        if paPacket.is_valid():
            validity = "valid"
        else:
            validity = "invalid"
            
        print "Got " + validity + " port agent raw packet with data length " + str(paPacket.get_data_size()) + ": " + str(paPacket.get_data())

    def myGotError(self, errorString = "No error string passed in."):
        self.errorCallbackCalled = True
        print "Got error: " +  errorString + "\r\n"
                       
    def init_instrument_simulator(self):
        """
        Startup a TCP server that we can use as an instrument simulator
        """
        self._instrument_simulator = TCPSimulatorServer()
        self.addCleanup(self._instrument_simulator.close)

        # Wait for the simulator to bind to a port
        timeout = time.time() + 10
        while (timeout > time.time()):
            if (self._instrument_simulator.port > 0):
                log.debug("Instrument simulator initialized on port %s" % self._instrument_simulator.port)
                return

            log.debug("waiting for simulator to bind. sleeping")
            time.sleep(1)

        raise IDKException("Timeout waiting for simulator to bind")

    def init_port_agent(self):
        """
        @brief Launch the driver process and driver client.  This is used in the
        integration and qualification tests.  The port agent abstracts the physical
        interface with the instrument.
        @retval return the pid to the logger process
        """
        if (self.port_agent):
            log.error("Port agent already initialized")
            return

        log.debug("Startup Port Agent")

        #comm_config = self.get_comm_config()

        config = self.port_agent_config()
        log.debug("port agent config: %s" % config)

        port_agent = PortAgentProcess.launch_process(config, timeout = 60, test_mode = True)

        port = port_agent.get_data_port()
        pid  = port_agent.get_pid()

        log.info('Started port agent pid %s listening at port %s' % (pid, port))

        self.addCleanup(self.stop_port_agent)
        self.port_agent = port_agent
        return port

    def port_agent_config(self):
        """
        Overload the default port agent configuration so that
        it connects to a simulated TCP connection.
        """
        config = {
            'device_addr' : self.ipaddr,
            'device_port' : self.device_port,

            'command_port': self.cmd_port,
            'data_port': self.data_port,

            'process_type': PortAgentProcessType.UNIX,
            'log_level': 5,
        }

        # Override the instrument connection information.
        config['device_addr'] = 'localhost'
        config['device_port'] = self._instrument_simulator.port

        return config
    
    def test_start_paClient_no_port_agent(self):

        print "port agent client test begin"

        paClient = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        
        paClient.init_comms(self.myGotData, self.myGotRaw, self.myGotError)
        
        self.assertTrue(self.errorCallbackCalled)
    
    #@unittest.skip('Need to add instrument simulator (listener or instrument port')
    def test_start_paClient_with_port_agent(self):

        self.init_instrument_simulator()
        self.startPortAgent()

        paClient = PortAgentClient(self.ipaddr, self.data_port, self.cmd_port)
        
        paClient.init_comms(self.myGotData, self.myGotRaw, self.myGotError)
        
        data = "this is a great big test"
        paClient.send(data)
        
        time.sleep(1)

        self._instrument_simulator.send(data)
        
        time.sleep(5)

        paClient.stop_comms()
        
        self.assertTrue(self.rawCallbackCalled)
        self.assertTrue(self.dataCallbackCalled)