def _getAndCheckSentNotifications(self, notifications_to_send):
        """Return the notifications that were successfully sent.

        It calls get_email_notifications() with the supplied
        notifications and return the ones that were actually sent. It
        also checks that the notifications got sent to the correct
        addresses.
        """
        email_notifications = get_email_notifications(notifications_to_send)
        to_addresses = set()
        sent_notifications = []
        for notifications, omitted, messages in email_notifications:
            for message in messages:
                to_addresses.add(message['to'])
            recipients = {}
            for notification in notifications:
                for recipient in notification.recipients:
                    for address in get_contact_email_addresses(
                        recipient.person):
                        recipients[address] = recipient
            expected_to_addresses = recipients.keys()
            self.assertEqual(
                sorted(expected_to_addresses), sorted(to_addresses))
            sent_notifications += notifications
        return sent_notifications
    def main(self):
        notifications_sent = False
        bug_notification_set = getUtility(IBugNotificationSet)
        deferred_notifications = \
            bug_notification_set.getDeferredNotifications()
        process_deferred_notifications(deferred_notifications)
        pending_notifications = get_email_notifications(
            bug_notification_set.getNotificationsToSend())
        for (bug_notifications,
             omitted_notifications,
             messages) in pending_notifications:
            for message in messages:
                self.logger.info("Notifying %s about bug %d." % (
                    message['To'], bug_notifications[0].bug.id))
                sendmail(message)
                self.logger.debug(message.as_string())
            for notification in bug_notifications:
                notification.date_emailed = UTC_NOW
                notification.status = BugNotificationStatus.SENT
            for notification in omitted_notifications:
                notification.date_emailed = UTC_NOW
                notification.status = BugNotificationStatus.OMITTED
            notifications_sent = True
            # Commit after each batch of email sent, so that we won't
            # re-mail the notifications in case of something going wrong
            # in the middle.
            self.txn.commit()

        if not notifications_sent:
            self.logger.debug("No notifications are pending to be sent.")
 def get_messages(self):
     notifications = self.notification_set.getNotificationsToSend()
     email_notifications = get_email_notifications(notifications)
     for (bug_notifications,
          omitted_notifications,
          messages) in email_notifications:
         for message in messages:
             yield message, message.get_payload(decode=True)
    def test_early_exit(self):
        # When not-yet-exhausted generators need to be deallocated Python
        # raises a GeneratorExit exception at the point of their last yield.
        # The get_email_notifications generator was catching that exception in
        # a try/except and logging it, leading to bug 994694.  This test
        # verifies that the fix for that bug (re-raising the exception) stays
        # in place.

        # Set up logging so we can later assert that no exceptions are logged.
        log_output = StringIO.StringIO()
        logger = logging.getLogger()
        log_handler = logging.StreamHandler(log_output)
        logger.addHandler(logging.StreamHandler(log_output))
        self.addCleanup(logger.removeHandler, log_handler)

        # Make some data to feed to get_email_notifications.
        person = getUtility(IPersonSet).getByEmail('*****@*****.**')
        msg = getUtility(IMessageSet).fromText('', '', owner=person)
        bug = MockBug(1, person)
        # We need more than one notification because we want the generator to
        # stay around after being started.  Consuming the first starts it but
        # since the second exists, the generator stays active.
        notifications = [
            MockBugNotification(
                message=msg, bug=bug, is_comment=True, date_emailed=None),
            MockBugNotification(
                message=msg, bug=bug, is_comment=True, date_emailed=None),
            ]

        # Now we create the generator, start it, and then close it, triggering
        # a GeneratorExit exception inside the generator.
        email_notifications = get_email_notifications(notifications)
        email_notifications.next()
        email_notifications.close()

        # Verify that no "Error while building email notifications." is logged.
        self.assertEqual('', log_output.getvalue())