class TestPing(unittest.TestCase):
        def setUp(self):
            if False:
                # debug leftover reactor events
                import twisted.internet.base
                twisted.internet.base.DelayedCall.debug = True

            self.factory = WebSocketServerFactory(protocols=['wamp.2.json'])
            self.factory.protocol = WebSocketServerProtocol
            self.factory.doStart()

            self.proto = self.factory.buildProtocol(IPv4Address('TCP', '127.0.0.1', 65534))
            self.transport = MagicMock()
            self.proto.transport = self.transport
            self.proto.connectionMade()

        def tearDown(self):
            self.factory.doStop()
            # not really necessary, but ...
            del self.factory
            del self.proto

        def test_unclean_timeout(self):
            """
            make a delayed call to drop the connection
            """
            # first we have to drive the protocol to STATE_CLOSING
            # ... which we achieve by sendCloseFrame after we're in
            # STATE_OPEN
            # XXX double-check this is the correct code-path to get here
            # "normally"?

            if False:
                self.proto.debug = True
                self.proto.factory._log = print

            # get to STATE_OPEN
            self.proto.data = mock_handshake_client
            self.proto.processHandshake()
            self.assertTrue(self.proto.state == WebSocketServerProtocol.STATE_OPEN)

            with replace_loop(Clock()) as reactor:
                # now 'do the test' and transition to CLOSING
                self.proto.sendCloseFrame()

                # check we scheduled a call
                self.assertEqual(len(reactor.calls), 1)
                self.assertEqual(reactor.calls[0].func, self.proto.onCloseHandshakeTimeout)
                self.assertEqual(reactor.calls[0].getTime(), self.proto.closeHandshakeTimeout)

                # now, advance the clock past the call (and thereby
                # execute it)
                reactor.advance(self.proto.closeHandshakeTimeout + 1)

                # we should have called abortConnection
                self.assertEqual("call.abortConnection()", str(self.proto.transport.method_calls[-1]))
                self.assertTrue(self.proto.transport.abortConnection.called)
                # ...too "internal" for an assert?
                self.assertEqual(self.proto.state, WebSocketServerProtocol.STATE_CLOSED)

        def test_auto_pingpong_timeout(self):
            """
            autoping and autoping-timeout timing
            """
            if False:
                self.proto.debug = True
                self.proto.factory._log = print
                self.proto.debugCodePaths = True

            # options are evaluated in succeedHandshake, called below
            self.proto.autoPingInterval = 5
            self.proto.autoPingTimeout = 2

            with replace_loop(Clock()) as reactor:
                # get to STATE_OPEN
                self.proto.data = mock_handshake_client
                self.proto.processHandshake()
                self.assertTrue(self.proto.state == WebSocketServerProtocol.STATE_OPEN)

                # we should have scheduled an autoPing
                self.assertEqual(1, len(reactor.calls))
                self.assertEqual(self.proto._sendAutoPing, reactor.calls[0].func)
                # ^^ un-unit-testy to assert on internal method?

                # advance past first auto-ping timeout
                reactor.advance(5)

                # first element from args tuple from transport.write()
                # call is our data
                self.assertTrue(self.transport.write.called)
                data = self.transport.write.call_args[0][0]

                # the opcode is the lower 7 bits of the first byte.
                (opcode,) = struct.unpack("B", data[0])
                opcode = opcode & (~0x80)

                # ... and should be "9" for ping
                self.assertEqual(9, opcode)

                # Because we have autoPingTimeout there should be
                # another delayed-called created now
                self.assertEqual(1, len(reactor.calls))
                self.assertEqual(self.proto.onAutoPingTimeout, reactor.calls[0].func)
                self.assertNotEqual(self.proto.state, self.proto.STATE_CLOSED)

                # ...which we'll now cause to trigger, aborting the connection
                reactor.advance(3)
                self.assertEqual(self.proto.state, self.proto.STATE_CLOSED)

        def test_auto_ping_got_pong(self):
            """
            auto-ping with correct reply cancels timeout
            """
            if False:
                self.proto.debug = True
                self.proto.factory._log = print
                self.proto.debugCodePaths = True

            # options are evaluated in succeedHandshake, called below
            self.proto.autoPingInterval = 5
            self.proto.autoPingTimeout = 2

            with replace_loop(Clock()) as reactor:
                # get to STATE_OPEN
                self.proto.data = mock_handshake_client
                self.proto.processHandshake()
                self.assertTrue(self.proto.state == WebSocketServerProtocol.STATE_OPEN)

                # we should have scheduled an autoPing
                self.assertEqual(1, len(reactor.calls))
                self.assertEqual(self.proto._sendAutoPing, reactor.calls[0].func)
                # ^^ un-unit-testy to assert on internal method?

                # advance past first auto-ping timeout
                reactor.advance(5)

                # should have an auto-ping timeout scheduled, and we
                # save it for later (to check it got cancelled)
                self.assertEqual(1, len(reactor.calls))
                self.assertEqual(self.proto.onAutoPingTimeout, reactor.calls[0].func)
                timeout_call = reactor.calls[0]

                # elsewhere we check that we actually send an opcode-9
                # message; now we just blindly inject our own reply
                # with a PONG frame

                frame = create_client_frame(opcode=10, payload=self.proto.autoPingPending)
                self.proto.data = frame
                # really needed twice; does header first, then rest
                self.proto.processData()
                self.proto.processData()

                # which should have cancelled the call
                self.assertTrue(timeout_call.cancelled)
    class TestPing(unittest.TestCase):
        def setUp(self):
            self.factory = WebSocketServerFactory(protocols=['wamp.2.json'])
            self.factory.protocol = WebSocketServerProtocol
            self.factory.doStart()

            self.proto = self.factory.buildProtocol(
                IPv4Address('TCP', '127.0.0.1', 65534))
            self.transport = MagicMock()
            self.proto.transport = self.transport
            self.proto.connectionMade()

        def tearDown(self):
            if self.proto.openHandshakeTimeoutCall:
                self.proto.openHandshakeTimeoutCall.cancel()
            self.factory.doStop()
            # not really necessary, but ...
            del self.factory
            del self.proto

        def test_unclean_timeout(self):
            """
            make a delayed call to drop the connection
            """
            # first we have to drive the protocol to STATE_CLOSING
            # ... which we achieve by sendCloseFrame after we're in
            # STATE_OPEN
            # XXX double-check this is the correct code-path to get here
            # "normally"?

            # get to STATE_OPEN
            self.proto.data = mock_handshake_client
            self.proto.processHandshake()
            self.assertTrue(
                self.proto.state == WebSocketServerProtocol.STATE_OPEN)

            with replace_loop(Clock()) as reactor:
                # now 'do the test' and transition to CLOSING
                self.proto.sendCloseFrame()

                # check we scheduled a call
                self.assertEqual(len(reactor.calls), 1)

                # now, advance the clock past the call (and thereby
                # execute it)
                reactor.advance(self.proto.closeHandshakeTimeout + 1)

                # we should have called abortConnection
                self.assertEqual("call.abortConnection()",
                                 str(self.proto.transport.method_calls[-1]))
                self.assertTrue(self.proto.transport.abortConnection.called)
                # ...too "internal" for an assert?
                self.assertEqual(self.proto.state,
                                 WebSocketServerProtocol.STATE_CLOSED)

        def test_auto_pingpong_timeout(self):
            """
            autoping and autoping-timeout timing
            """
            # options are evaluated in succeedHandshake, called below
            self.proto.autoPingInterval = 5
            self.proto.autoPingTimeout = 2

            with replace_loop(Clock()) as reactor:
                # get to STATE_OPEN
                self.proto.data = mock_handshake_client
                self.proto.processHandshake()
                self.assertTrue(
                    self.proto.state == WebSocketServerProtocol.STATE_OPEN)

                # we should have scheduled an autoPing
                self.assertEqual(1, len(reactor.calls))

                # advance past first auto-ping timeout
                reactor.advance(5)

                # first element from args tuple from transport.write()
                # call is our data
                self.assertTrue(self.transport.write.called)
                data = self.transport.write.call_args[0][0]

                if PY3:
                    _data = bytes([data[0]])
                else:
                    _data = data[0]

                # the opcode is the lower 7 bits of the first byte.
                (opcode, ) = struct.unpack("B", _data)
                opcode = opcode & (~0x80)

                # ... and should be "9" for ping
                self.assertEqual(9, opcode)

                # Because we have autoPingTimeout there should be
                # another delayed-called created now
                self.assertEqual(1, len(reactor.calls))
                self.assertNotEqual(self.proto.state, self.proto.STATE_CLOSED)

                # ...which we'll now cause to trigger, aborting the connection
                reactor.advance(3)
                self.assertEqual(self.proto.state, self.proto.STATE_CLOSED)

        def test_auto_ping_got_pong(self):
            """
            auto-ping with correct reply cancels timeout
            """
            # options are evaluated in succeedHandshake, called below
            self.proto.autoPingInterval = 5
            self.proto.autoPingTimeout = 2

            with replace_loop(Clock()) as reactor:
                # get to STATE_OPEN
                self.proto.data = mock_handshake_client
                self.proto.processHandshake()
                self.assertTrue(
                    self.proto.state == WebSocketServerProtocol.STATE_OPEN)

                # we should have scheduled an autoPing
                self.assertEqual(1, len(reactor.calls))

                # advance past first auto-ping timeout
                reactor.advance(5)

                # should have an auto-ping timeout scheduled, and we
                # save it for later (to check it got cancelled)
                self.assertEqual(1, len(reactor.calls))
                timeout_call = reactor.calls[0]

                # elsewhere we check that we actually send an opcode-9
                # message; now we just blindly inject our own reply
                # with a PONG frame

                frame = create_client_frame(opcode=10,
                                            payload=self.proto.autoPingPending)
                self.proto.data = frame
                # really needed twice; does header first, then rest
                self.proto.processData()
                self.proto.processData()

                # which should have cancelled the call
                self.assertTrue(timeout_call.cancelled)