|
@@ -168,14 +168,16 @@ class MsgQTest(unittest.TestCase):
|
|
|
undeliverable notifications under the correct circumstances.
|
|
|
"""
|
|
|
sent_messages = []
|
|
|
- def fake_end_prepared_msg(socket, msg):
|
|
|
+ def fake_send_prepared_msg(socket, msg):
|
|
|
sent_messages.append((socket, msg))
|
|
|
- self.__msgq.send_prepared_msg = fake_end_prepared_msg
|
|
|
+ return True
|
|
|
+ self.__msgq.send_prepared_msg = fake_send_prepared_msg
|
|
|
# These would be real sockets in the MsgQ, but we pass them as
|
|
|
# parameters only, so we don't need them to be. We use simple
|
|
|
# integers to tell one from another.
|
|
|
sender = 1
|
|
|
recipient = 2
|
|
|
+ another_recipiet = 3
|
|
|
# The routing headers and data to test with.
|
|
|
routing = {
|
|
|
'to': '*',
|
|
@@ -255,6 +257,39 @@ class MsgQTest(unittest.TestCase):
|
|
|
self.assertEqual(2, sent_messages[0][0]) # The recipient
|
|
|
self.assertEqual((routing, data), self.parse_msg(sent_messages[0][1]))
|
|
|
sent_messages = []
|
|
|
+ # If an attempt to send fails, consider it no recipient.
|
|
|
+ def fail_send_prepared_msg(socket, msg):
|
|
|
+ '''
|
|
|
+ Pretend sending a message failed. After one call, return to the
|
|
|
+ usual mock, so the errors or other messages can be sent.
|
|
|
+ '''
|
|
|
+ self.__msgq.send_prepared_msg = fake_send_prepared_msg
|
|
|
+ self.__msgq.send_prepared_msg = fail_send_prepared_msg
|
|
|
+ self.__msgq.process_command_send(sender, routing, data)
|
|
|
+ self.assertEqual(1, len(sent_messages))
|
|
|
+ self.assertEqual(1, sent_messages[0][0])
|
|
|
+ self.assertEqual(({
|
|
|
+ 'group': 'group',
|
|
|
+ 'instance': '*',
|
|
|
+ 'reply': 42,
|
|
|
+ 'seq': 42,
|
|
|
+ 'from': 'msgq',
|
|
|
+ 'to': 'sender',
|
|
|
+ 'want_answer': True
|
|
|
+ }, {'result': [-1, "No such recipient"]}),
|
|
|
+ self.parse_msg(sent_messages[0][1]))
|
|
|
+ sent_messages = []
|
|
|
+ # But if there are more recipients and only one fails, it should
|
|
|
+ # be delivered to the other and not considered an error
|
|
|
+ self.__msgq.send_prepared_msg = fail_send_prepared_msg
|
|
|
+ routing["to"] = '*'
|
|
|
+ self.__msgq.subs.find = lambda group, instance: [recipient,
|
|
|
+ another_recipiet]
|
|
|
+ self.__msgq.process_command_send(sender, routing, data)
|
|
|
+ self.assertEqual(1, len(sent_messages))
|
|
|
+ self.assertEqual(3, sent_messages[0][0]) # The recipient
|
|
|
+ self.assertEqual((routing, data), self.parse_msg(sent_messages[0][1]))
|
|
|
+ sent_messages = []
|
|
|
|
|
|
class DummySocket:
|
|
|
"""
|
|
@@ -360,17 +395,27 @@ class SendNonblock(unittest.TestCase):
|
|
|
self.assertEqual(0, status,
|
|
|
"The task did not complete successfully in time")
|
|
|
|
|
|
+ def get_msgq_with_sockets(self):
|
|
|
+ '''
|
|
|
+ Create a message queue and prepare it for use with a socket pair.
|
|
|
+ The write end is put into the message queue, so we can check it.
|
|
|
+ It returns (msgq, read_end, write_end). It is expected the sockets
|
|
|
+ are closed by the caller afterwards.
|
|
|
+ '''
|
|
|
+ msgq = MsgQ()
|
|
|
+ # We do only partial setup, so we don't create the listening socket
|
|
|
+ msgq.setup_poller()
|
|
|
+ (read, write) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
|
+ msgq.register_socket(write)
|
|
|
+ return (msgq, read, write)
|
|
|
+
|
|
|
def infinite_sender(self, sender):
|
|
|
"""
|
|
|
Sends data until an exception happens. socket.error is caught,
|
|
|
as it means the socket got closed. Sender is called to actually
|
|
|
send the data.
|
|
|
"""
|
|
|
- msgq = MsgQ()
|
|
|
- # We do only partial setup, so we don't create the listening socket
|
|
|
- msgq.setup_poller()
|
|
|
- (read, write) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
|
- msgq.register_socket(write)
|
|
|
+ (msgq, read, write) = self.get_msgq_with_sockets()
|
|
|
# Keep sending while it is not closed by the msgq
|
|
|
try:
|
|
|
while True:
|
|
@@ -406,6 +451,34 @@ class SendNonblock(unittest.TestCase):
|
|
|
self.terminate_check(lambda: self.infinite_sender(
|
|
|
lambda msgq, socket: msgq.send_prepared_msg(socket, data)))
|
|
|
|
|
|
+ def test_sendprepared_success(self):
|
|
|
+ '''
|
|
|
+ Test the send_prepared_msg returns success when queueing messages.
|
|
|
+ It does so on the first attempt (when it actually tries to send
|
|
|
+ something to the socket) and on any attempt that follows and the
|
|
|
+ buffer is already full.
|
|
|
+ '''
|
|
|
+ (msgq, read, write) = self.get_msgq_with_sockets()
|
|
|
+ # Now keep sending until we fill in something into the internal
|
|
|
+ # buffer.
|
|
|
+ while not write.fileno() in msgq.sendbuffs:
|
|
|
+ self.assertTrue(msgq.send_prepared_msg(write, b'data'))
|
|
|
+ read.close()
|
|
|
+ write.close()
|
|
|
+
|
|
|
+ def test_sendprepared_epipe(self):
|
|
|
+ '''
|
|
|
+ Test the send_prepared_msg returns false when we try to queue a
|
|
|
+ message and the other side is not there any more. It should be done
|
|
|
+ with EPIPE, so not a fatal error.
|
|
|
+ '''
|
|
|
+ (msgq, read, write) = self.get_msgq_with_sockets()
|
|
|
+ # Close one end. It should make a EPIPE on the other.
|
|
|
+ read.close()
|
|
|
+ # Now it should soft-fail
|
|
|
+ self.assertFalse(msgq.send_prepared_msg(write, b'data'))
|
|
|
+ write.close()
|
|
|
+
|
|
|
def send_many(self, data):
|
|
|
"""
|
|
|
Tries that sending a command many times and getting an answer works.
|