def test_exception_raised_by_timeout(self):
        herald = DeliveryHerald()
        notified_ok = True

        def fn():
            try:
                sleep(1.5)
                herald.notify_delivered(MessageId(halink_ctx=43, tag=3))
            except:
                logging.exception('*** ERROR ***')
                notified_ok = False

        t = Thread(target=fn)
        t.start()

        m = MessageId
        try:
            with self.assertRaises(NotDelivered):
                herald.wait_for_any(HaLinkMessagePromise(
                    [m(42, 1), m(42, 3), m(42, 4)]),
                                    timeout_sec=5)
        finally:
            t.join()
        self.assertTrue(notified_ok,
                        'Unexpected exception appeared in notifier thread')
    def test_works_under_load(self):
        herald = DeliveryHerald()
        notified_ok = True

        def fn(msg: MessageId):
            try:
                sleep(1.5)
                herald.notify_delivered(msg)
            except:
                logging.exception('*** ERROR ***')
                notified_ok = False

        threads = [
            Thread(target=fn, args=(MessageId(100, i), ))
            for i in range(1, 32)
        ]
        for t in threads:
            t.start()

        def m(x):
            return MessageId(halink_ctx=100, tag=x)

        try:
            herald.wait_for_any(HaLinkMessagePromise(
                [m(99), m(25), m(28), m(31)]),
                                timeout_sec=5)
        finally:
            for t in threads:
                t.join()
        self.assertTrue(notified_ok,
                        'Unexpected exception appeared in notifier thread')
    def test_if_delivered_earlier_than_awaited_works_immediately(self):
        herald = DeliveryHerald()
        notified_ok = True
        thread_count = 1
        latch = CountDownLatch(thread_count)

        def fn(msg: MessageId):
            try:
                LOG.debug('Thread started')
                herald.notify_delivered(msg)
                LOG.debug('Notified delivery %s', msg)
                latch.count_down()
                LOG.debug('Main thread unblocked')

            except:
                logging.exception('*** ERROR ***')
                notified_ok = False

        threads = [
            Thread(target=fn, args=(MessageId(100, i + 1), ))
            for i in range(thread_count)
        ]

        for t in threads:
            t.start()
        # Block until all the threads come to latch.count_down() and thus
        # the message is notified for sure
        latch.await()

        def m(x):
            return MessageId(halink_ctx=100, tag=x)

        try:
            started = time()
            herald.wait_for_any(HaLinkMessagePromise([m(1)]), timeout_sec=5)
            finished = time()
        finally:
            for t in threads:
                t.join()
        self.assertTrue(notified_ok,
                        'Unexpected exception appeared in notifier thread')
        self.assertLess(
            finished - started, 5,
            'Awaiting thread was unblocked only by a timeout. It means '
            'that unsorted_deliveries was analyzed too late.'
        )
    def test_if_delivered_earlier_than_awaited_wait_many(self):
        herald = DeliveryHerald()
        notified_ok = True
        thread_count = 6
        latch = CountDownLatch(thread_count)

        def fn(msg: MessageId):
            try:
                LOG.debug('Thread started')
                herald.notify_delivered(msg)
                LOG.debug('Notified delivery %s', msg)
                latch.count_down()
                LOG.debug('Main thread unblocked')

            except:
                logging.exception('*** ERROR ***')
                notified_ok = False

        threads = [
            Thread(target=fn, args=(MessageId(100, i + 1), ))
            for i in range(thread_count)
        ]

        for t in threads:
            t.start()
        # Block until all the threads come to latch.count_down() and thus
        # the message is notified for sure
        latch.await()

        def m(x):
            return MessageId(halink_ctx=100, tag=x)

        try:
            herald.wait_for_any(HaLinkMessagePromise([m(1), m(5)]),
                                timeout_sec=2)
        finally:
            for t in threads:
                t.join()
        self.assertTrue(notified_ok,
                        'Unexpected exception appeared in notifier thread')
        self.assertEqual(4, len(herald.unsorted_deliveries.keys()))
    def test_it_works(self):
        herald = DeliveryHerald()
        notified_ok = True

        def fn():
            try:
                sleep(1.5)
                herald.notify_delivered(MessageId(halink_ctx=100, tag=1))
            except:
                logging.exception('*** ERROR ***')
                notified_ok = False

        t = Thread(target=fn)
        t.start()

        m = MessageId
        herald.wait_for_any(HaLinkMessagePromise(
            [m(100, 1), m(100, 3), m(100, 4)]),
                            timeout_sec=10)
        t.join()
        self.assertTrue(notified_ok,
                        'Unexpected exception appeared in notifier thread')