def test_reload(self):
     # reload() loads the given objects using queries generated by
     # gen_reload_queries().
     db_object = self.factory.makeComponent()
     db_object_naked = proxy.removeSecurityProxy(db_object)
     db_object_info = get_obj_info(db_object_naked)
     IStore(db_object).flush()
     self.failUnlessEqual(None, db_object_info.get('invalidated'))
     IStore(db_object).invalidate(db_object)
     self.failUnlessEqual(True, db_object_info.get('invalidated'))
     bulk.reload([db_object])
     self.failUnlessEqual(None, db_object_info.get('invalidated'))
Exemple #2
0
 def test_reload(self):
     # reload() loads the given objects using queries generated by
     # gen_reload_queries().
     db_object = self.factory.makeComponent()
     db_object_naked = proxy.removeSecurityProxy(db_object)
     db_object_info = get_obj_info(db_object_naked)
     IStore(db_object).flush()
     self.failUnlessEqual(None, db_object_info.get('invalidated'))
     IStore(db_object).invalidate(db_object)
     self.failUnlessEqual(True, db_object_info.get('invalidated'))
     bulk.reload([db_object])
     self.failUnlessEqual(None, db_object_info.get('invalidated'))
Exemple #3
0
    def updateBugWatches(self, remotesystem, bug_watches_to_update, now=None,
                         batch_size=None):
        """Update the given bug watches."""
        # Save the url for later, since we might need it to report an
        # error after a transaction has been aborted.
        bug_tracker_url = remotesystem.baseurl

        # Some tests pass a list of bug watches whilst checkwatches.py
        # will pass a SelectResults instance. We convert bug_watches to a
        # list here to ensure that were're doing sane things with it
        # later on.
        with self.transaction:
            bug_watches = list(bug_watches_to_update)
            bug_watch_ids = [bug_watch.id for bug_watch in bug_watches]

        # Fetch the time on the server. We'll use this in
        # _getRemoteIdsToCheck() and when determining whether we can
        # sync comments or not.
        with record_errors(self.transaction, bug_watch_ids):
            server_time = remotesystem.getCurrentDBTime()
            remote_ids = self._getRemoteIdsToCheck(
                remotesystem, bug_watches, server_time, now, batch_size)

        remote_ids_to_check = remote_ids['remote_ids_to_check']
        all_remote_ids = remote_ids['all_remote_ids']
        unmodified_remote_ids = remote_ids['unmodified_remote_ids']

        # Remove from the list of bug watches any watch whose remote ID
        # doesn't appear in the list of IDs to check.
        with self.transaction:
            reload(bug_watches)
            for bug_watch in list(bug_watches):
                if bug_watch.remotebug not in remote_ids_to_check:
                    bug_watches.remove(bug_watch)

        self.logger.info(
            "Updating %i watches for %i bugs on %s" % (
                len(bug_watches), len(remote_ids_to_check), bug_tracker_url))

        with record_errors(self.transaction, bug_watch_ids):
            remotesystem.initializeRemoteBugDB(remote_ids_to_check)

        for remote_bug_id in all_remote_ids:
            remote_bug_updater = self.remote_bug_updater_factory(
                self, remotesystem, remote_bug_id, bug_watch_ids,
                unmodified_remote_ids, server_time)
            remote_bug_updater.updateRemoteBug()
Exemple #4
0
    def updateBugWatches(self, remotesystem, bug_watches_to_update, now=None,
                         batch_size=None):
        """Update the given bug watches."""
        # Save the url for later, since we might need it to report an
        # error after a transaction has been aborted.
        bug_tracker_url = remotesystem.baseurl

        # Some tests pass a list of bug watches whilst checkwatches.py
        # will pass a SelectResults instance. We convert bug_watches to a
        # list here to ensure that were're doing sane things with it
        # later on.
        with self.transaction:
            bug_watches = list(bug_watches_to_update)
            bug_watch_ids = [bug_watch.id for bug_watch in bug_watches]

        # Fetch the time on the server. We'll use this in
        # _getRemoteIdsToCheck() and when determining whether we can
        # sync comments or not.
        with record_errors(self.transaction, bug_watch_ids):
            server_time = remotesystem.getCurrentDBTime()
            remote_ids = self._getRemoteIdsToCheck(
                remotesystem, bug_watches, server_time, now, batch_size)

        remote_ids_to_check = remote_ids['remote_ids_to_check']
        all_remote_ids = remote_ids['all_remote_ids']
        unmodified_remote_ids = remote_ids['unmodified_remote_ids']

        # Remove from the list of bug watches any watch whose remote ID
        # doesn't appear in the list of IDs to check.
        with self.transaction:
            reload(bug_watches)
            for bug_watch in list(bug_watches):
                if bug_watch.remotebug not in remote_ids_to_check:
                    bug_watches.remove(bug_watch)

        self.logger.info(
            "Updating %i watches for %i bugs on %s" % (
                len(bug_watches), len(remote_ids_to_check), bug_tracker_url))

        with record_errors(self.transaction, bug_watch_ids):
            remotesystem.initializeRemoteBugDB(remote_ids_to_check)

        for remote_bug_id in all_remote_ids:
            remote_bug_updater = self.remote_bug_updater_factory(
                self, remotesystem, remote_bug_id, bug_watch_ids,
                unmodified_remote_ids, server_time)
            remote_bug_updater.updateRemoteBug()
 def test_source_overrides_constant_query_count(self):
     # The query count is constant, no matter how many sources are
     # checked.
     spns = []
     distroseries = self.factory.makeDistroSeries()
     pocket = self.factory.getAnyPocket()
     for i in xrange(10):
         spph = self.factory.makeSourcePackagePublishingHistory(
             distroseries=distroseries, archive=distroseries.main_archive,
             pocket=pocket)
         spns.append(spph.sourcepackagerelease.sourcepackagename)
     flush_database_caches()
     distroseries.main_archive
     bulk.reload(spns)
     policy = FromExistingOverridePolicy()
     with StormStatementRecorder() as recorder:
         policy.calculateSourceOverrides(
             spph.distroseries.main_archive, spph.distroseries,
             spph.pocket, spns)
     self.assertThat(recorder, HasQueryCount(Equals(4)))
 def test_binary_overrides_constant_query_count(self):
     # The query count is constant, no matter how many bpn-das pairs are
     # checked.
     bpns = []
     distroarchseries = self.factory.makeDistroArchSeries()
     distroseries = distroarchseries.distroseries
     distroseries.nominatedarchindep = distroarchseries
     pocket = self.factory.getAnyPocket()
     for i in xrange(10):
         bpph = self.factory.makeBinaryPackagePublishingHistory(
             distroarchseries=distroarchseries,
             archive=distroseries.main_archive, pocket=pocket)
         bpns.append((bpph.binarypackagerelease.binarypackagename, None))
     flush_database_caches()
     distroseries.main_archive
     bulk.reload(bpn[0] for bpn in bpns)
     policy = FromExistingOverridePolicy()
     with StormStatementRecorder() as recorder:
         policy.calculateBinaryOverrides(
             distroseries.main_archive, distroseries, pocket, bpns)
     self.assertThat(recorder, HasQueryCount(Equals(4)))
Exemple #7
0
    def _getRemoteIdsToCheck(self, remotesystem, bug_watches,
                             server_time=None, now=None, batch_size=None):
        """Return the remote bug IDs to check for a set of bug watches.

        The remote bug tracker is queried to find out which of the
        remote bugs in `bug_watches` have changed since they were last
        checked. Those which haven't changed are excluded.

        :param bug_watches: A set of `BugWatch`es to be checked.
        :param remotesystem: The `ExternalBugtracker` on which
            `getModifiedRemoteBugs`() should be called
        :param server_time: The time according to the remote server.
            This may be None when the server doesn't specify a remote time.
        :param now: The current time (used for testing)
        :return: A list of remote bug IDs to be updated.
        """
        # Check that the remote server's notion of time agrees with
        # ours. If not, raise a TooMuchTimeSkew error, since if the
        # server's wrong about the time it'll mess up all our times when
        # we import things.
        if now is None:
            now = datetime.now(pytz.timezone('UTC'))

        if (server_time is not None and
            abs(server_time - now) > ACCEPTABLE_TIME_SKEW):
            raise TooMuchTimeSkew(abs(server_time - now))

        # We limit the number of watches we're updating by the
        # ExternalBugTracker's batch_size. In an ideal world we'd just
        # slice the bug_watches list but for the sake of testing we need
        # to ensure that the list of bug watches is ordered by remote
        # bug id before we do so.
        if batch_size is None:
            # If a batch_size hasn't been passed, use the one specified
            # by the ExternalBugTracker.
            batch_size = remotesystem.batch_size

        with self.transaction:
            reload(bug_watches)
            old_bug_watches = set(
                bug_watch for bug_watch in bug_watches
                if bug_watch.lastchecked is not None)
            if len(old_bug_watches) == 0:
                oldest_lastchecked = None
            else:
                oldest_lastchecked = min(
                    bug_watch.lastchecked for bug_watch in old_bug_watches)
                # Adjust for possible time skew, and some more, just to be
                # safe.
                oldest_lastchecked -= (
                    ACCEPTABLE_TIME_SKEW + timedelta(minutes=1))
            # Collate the remote IDs.
            remote_old_ids = sorted(
                set(bug_watch.remotebug for bug_watch in old_bug_watches))
            remote_new_ids = sorted(
                set(bug_watch.remotebug for bug_watch in bug_watches
                if bug_watch not in old_bug_watches))
            # If the remote system is not configured to sync comments,
            # don't bother checking for any to push.
            if remotesystem.sync_comments:
                remote_ids_with_comments = sorted(
                    bug_watch.remotebug for bug_watch in bug_watches
                    if bug_watch.unpushed_comments.any() is not None)
            else:
                remote_ids_with_comments = []

        # We only make the call to getModifiedRemoteBugs() if there
        # are actually some bugs that we're interested in so as to
        # avoid unnecessary network traffic.
        if server_time is not None and len(remote_old_ids) > 0:
            if batch_size == BATCH_SIZE_UNLIMITED:
                remote_old_ids_to_check = (
                    remotesystem.getModifiedRemoteBugs(
                        remote_old_ids, oldest_lastchecked))
            else:
                # Don't ask the remote system about more than
                # batch_size bugs at once, but keep asking until we
                # run out of bugs to ask about or we have batch_size
                # bugs to check.
                remote_old_ids_to_check = []
                for index in range(0, len(remote_old_ids), batch_size):
                    remote_old_ids_to_check.extend(
                        remotesystem.getModifiedRemoteBugs(
                            remote_old_ids[index:index + batch_size],
                            oldest_lastchecked))
                    if len(remote_old_ids_to_check) >= batch_size:
                        break
        else:
            remote_old_ids_to_check = remote_old_ids

        # We'll create our remote_ids_to_check list so that it's
        # prioritized. We include remote IDs in priority order:
        #  1. IDs with comments.
        #  2. IDs that haven't been checked.
        #  3. Everything else.
        remote_ids_to_check = chain(
            remote_ids_with_comments, remote_new_ids, remote_old_ids_to_check)

        if batch_size != BATCH_SIZE_UNLIMITED:
            # Some remote bug IDs may appear in more than one list so
            # we must filter the list before slicing.
            remote_ids_to_check = islice(
                unique(remote_ids_to_check), batch_size)

        # Stuff the IDs in a set.
        remote_ids_to_check = set(remote_ids_to_check)

        # Make sure that unmodified_remote_ids only includes IDs that
        # could have been checked but which weren't modified on the
        # remote server and which haven't been listed for checking
        # otherwise (i.e. because they have comments to be pushed).
        unmodified_remote_ids = set(remote_old_ids)
        unmodified_remote_ids.difference_update(remote_old_ids_to_check)
        unmodified_remote_ids.difference_update(remote_ids_to_check)

        all_remote_ids = remote_ids_to_check.union(unmodified_remote_ids)
        return {
            'remote_ids_to_check': sorted(remote_ids_to_check),
            'all_remote_ids': sorted(all_remote_ids),
            'unmodified_remote_ids': sorted(unmodified_remote_ids),
            }
Exemple #8
0
    def _getExternalBugTrackersAndWatches(self, bug_tracker, bug_watches):
        """Return an `ExternalBugTracker` instance for `bug_tracker`."""
        with self.transaction:
            num_watches = bug_tracker.watches.count()
            remotesystem = (
                externalbugtracker.get_external_bugtracker(bug_tracker))
            # We special-case the Gnome Bugzilla.
            is_gnome_bugzilla = bug_tracker == (
                getUtility(ILaunchpadCelebrities).gnome_bugzilla)

        # Probe the remote system for additional capabilities.
        remotesystem_to_use = remotesystem.getExternalBugTrackerToUse()

        # Try to hint at how many bug watches to check each time.
        suggest_batch_size(remotesystem_to_use, num_watches)

        if (is_gnome_bugzilla and remotesystem_to_use.sync_comments):
            # If there are no products to sync comments for, disable
            # comment sync and return.
            if len(self._syncable_gnome_products) == 0:
                remotesystem_to_use.sync_comments = False
                return [
                    (remotesystem_to_use, bug_watches),
                    ]

            syncable_watches = []
            other_watches = []

            with self.transaction:
                reload(bug_watches)
                remote_bug_ids = [
                    bug_watch.remotebug for bug_watch in bug_watches]

            remote_products = (
                remotesystem_to_use.getProductsForRemoteBugs(
                    remote_bug_ids))

            with self.transaction:
                reload(bug_watches)
                for bug_watch in bug_watches:
                    if (remote_products.get(bug_watch.remotebug) in
                        self._syncable_gnome_products):
                        syncable_watches.append(bug_watch)
                    else:
                        other_watches.append(bug_watch)

            # For bug watches on remote bugs that are against products
            # in the _syncable_gnome_products list - i.e. ones with which
            # we want to sync comments - we return a BugzillaAPI
            # instance with sync_comments=True, otherwise we return a
            # similar BugzillaAPI instance, but with sync_comments=False.
            remotesystem_for_syncables = remotesystem_to_use
            remotesystem_for_others = copy(remotesystem_to_use)
            remotesystem_for_others.sync_comments = False

            return [
                (remotesystem_for_syncables, syncable_watches),
                (remotesystem_for_others, other_watches),
                ]
        else:
            return [
                (remotesystem_to_use, bug_watches),
                ]
Exemple #9
0
    def _getRemoteIdsToCheck(self, remotesystem, bug_watches,
                             server_time=None, now=None, batch_size=None):
        """Return the remote bug IDs to check for a set of bug watches.

        The remote bug tracker is queried to find out which of the
        remote bugs in `bug_watches` have changed since they were last
        checked. Those which haven't changed are excluded.

        :param bug_watches: A set of `BugWatch`es to be checked.
        :param remotesystem: The `ExternalBugtracker` on which
            `getModifiedRemoteBugs`() should be called
        :param server_time: The time according to the remote server.
            This may be None when the server doesn't specify a remote time.
        :param now: The current time (used for testing)
        :return: A list of remote bug IDs to be updated.
        """
        # Check that the remote server's notion of time agrees with
        # ours. If not, raise a TooMuchTimeSkew error, since if the
        # server's wrong about the time it'll mess up all our times when
        # we import things.
        if now is None:
            now = datetime.now(pytz.timezone('UTC'))

        if (server_time is not None and
            abs(server_time - now) > ACCEPTABLE_TIME_SKEW):
            raise TooMuchTimeSkew(abs(server_time - now))

        # We limit the number of watches we're updating by the
        # ExternalBugTracker's batch_size. In an ideal world we'd just
        # slice the bug_watches list but for the sake of testing we need
        # to ensure that the list of bug watches is ordered by remote
        # bug id before we do so.
        if batch_size is None:
            # If a batch_size hasn't been passed, use the one specified
            # by the ExternalBugTracker.
            batch_size = remotesystem.batch_size

        with self.transaction:
            reload(bug_watches)
            old_bug_watches = set(
                bug_watch for bug_watch in bug_watches
                if bug_watch.lastchecked is not None)
            if len(old_bug_watches) == 0:
                oldest_lastchecked = None
            else:
                oldest_lastchecked = min(
                    bug_watch.lastchecked for bug_watch in old_bug_watches)
                # Adjust for possible time skew, and some more, just to be
                # safe.
                oldest_lastchecked -= (
                    ACCEPTABLE_TIME_SKEW + timedelta(minutes=1))
            # Collate the remote IDs.
            remote_old_ids = sorted(
                set(bug_watch.remotebug for bug_watch in old_bug_watches))
            remote_new_ids = sorted(
                set(bug_watch.remotebug for bug_watch in bug_watches
                if bug_watch not in old_bug_watches))
            # If the remote system is not configured to sync comments,
            # don't bother checking for any to push.
            if remotesystem.sync_comments:
                remote_ids_with_comments = sorted(
                    bug_watch.remotebug for bug_watch in bug_watches
                    if bug_watch.unpushed_comments.any() is not None)
            else:
                remote_ids_with_comments = []

        # We only make the call to getModifiedRemoteBugs() if there
        # are actually some bugs that we're interested in so as to
        # avoid unnecessary network traffic.
        if server_time is not None and len(remote_old_ids) > 0:
            if batch_size == BATCH_SIZE_UNLIMITED:
                remote_old_ids_to_check = (
                    remotesystem.getModifiedRemoteBugs(
                        remote_old_ids, oldest_lastchecked))
            else:
                # Don't ask the remote system about more than
                # batch_size bugs at once, but keep asking until we
                # run out of bugs to ask about or we have batch_size
                # bugs to check.
                remote_old_ids_to_check = []
                for index in xrange(0, len(remote_old_ids), batch_size):
                    remote_old_ids_to_check.extend(
                        remotesystem.getModifiedRemoteBugs(
                            remote_old_ids[index : index + batch_size],
                            oldest_lastchecked))
                    if len(remote_old_ids_to_check) >= batch_size:
                        break
        else:
            remote_old_ids_to_check = remote_old_ids

        # We'll create our remote_ids_to_check list so that it's
        # prioritized. We include remote IDs in priority order:
        #  1. IDs with comments.
        #  2. IDs that haven't been checked.
        #  3. Everything else.
        remote_ids_to_check = chain(
            remote_ids_with_comments, remote_new_ids, remote_old_ids_to_check)

        if batch_size != BATCH_SIZE_UNLIMITED:
            # Some remote bug IDs may appear in more than one list so
            # we must filter the list before slicing.
            remote_ids_to_check = islice(
                unique(remote_ids_to_check), batch_size)

        # Stuff the IDs in a set.
        remote_ids_to_check = set(remote_ids_to_check)

        # Make sure that unmodified_remote_ids only includes IDs that
        # could have been checked but which weren't modified on the
        # remote server and which haven't been listed for checking
        # otherwise (i.e. because they have comments to be pushed).
        unmodified_remote_ids = set(remote_old_ids)
        unmodified_remote_ids.difference_update(remote_old_ids_to_check)
        unmodified_remote_ids.difference_update(remote_ids_to_check)

        all_remote_ids = remote_ids_to_check.union(unmodified_remote_ids)
        return {
            'remote_ids_to_check': sorted(remote_ids_to_check),
            'all_remote_ids': sorted(all_remote_ids),
            'unmodified_remote_ids': sorted(unmodified_remote_ids),
            }
Exemple #10
0
    def _getExternalBugTrackersAndWatches(self, bug_tracker, bug_watches):
        """Return an `ExternalBugTracker` instance for `bug_tracker`."""
        with self.transaction:
            num_watches = bug_tracker.watches.count()
            remotesystem = (
                externalbugtracker.get_external_bugtracker(bug_tracker))
            # We special-case the Gnome Bugzilla.
            is_gnome_bugzilla = bug_tracker == (
                getUtility(ILaunchpadCelebrities).gnome_bugzilla)

        # Probe the remote system for additional capabilities.
        remotesystem_to_use = remotesystem.getExternalBugTrackerToUse()

        # Try to hint at how many bug watches to check each time.
        suggest_batch_size(remotesystem_to_use, num_watches)

        if (is_gnome_bugzilla and remotesystem_to_use.sync_comments):
            # If there are no products to sync comments for, disable
            # comment sync and return.
            if len(self._syncable_gnome_products) == 0:
                remotesystem_to_use.sync_comments = False
                return [
                    (remotesystem_to_use, bug_watches),
                    ]

            syncable_watches = []
            other_watches = []

            with self.transaction:
                reload(bug_watches)
                remote_bug_ids = [
                    bug_watch.remotebug for bug_watch in bug_watches]

            remote_products = (
                remotesystem_to_use.getProductsForRemoteBugs(
                    remote_bug_ids))

            with self.transaction:
                reload(bug_watches)
                for bug_watch in bug_watches:
                    if (remote_products.get(bug_watch.remotebug) in
                        self._syncable_gnome_products):
                        syncable_watches.append(bug_watch)
                    else:
                        other_watches.append(bug_watch)

            # For bug watches on remote bugs that are against products
            # in the _syncable_gnome_products list - i.e. ones with which
            # we want to sync comments - we return a BugzillaAPI
            # instance with sync_comments=True, otherwise we return a
            # similar BugzillaAPI instance, but with sync_comments=False.
            remotesystem_for_syncables = remotesystem_to_use
            remotesystem_for_others = copy(remotesystem_to_use)
            remotesystem_for_others.sync_comments = False

            return [
                (remotesystem_for_syncables, syncable_watches),
                (remotesystem_for_others, other_watches),
                ]
        else:
            return [
                (remotesystem_to_use, bug_watches),
                ]