launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03534
[Merge] lp:~allenap/launchpad/sync-button-bug-747546 into lp:launchpad/db-devel
Gavin Panella has proposed merging lp:~allenap/launchpad/sync-button-bug-747546 into lp:launchpad/db-devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #747546 in Launchpad itself: "Derived Distros: Add a button to sync all packages changed in parent but not changed in child"
https://bugs.launchpad.net/launchpad/+bug/747546
For more details, see:
https://code.launchpad.net/~allenap/launchpad/sync-button-bug-747546/+merge/60351
This branch adds a button to the +localpackagediffs page that syncs
all updates into a derived series for packages that have not been
changed in the derived series.
It is the completion of jtv's work in lp:~jtv/launchpad/bug-747546.
This branch is quite long, but a lot of it is lint fixes.
--
https://code.launchpad.net/~allenap/launchpad/sync-button-bug-747546/+merge/60351
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/sync-button-bug-747546 into lp:launchpad/db-devel.
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py 2011-05-06 10:45:50 +0000
+++ lib/lp/registry/browser/distroseries.py 2011-05-09 09:57:34 +0000
@@ -104,11 +104,20 @@
from lp.services.worlddata.interfaces.language import ILanguageSet
from lp.soyuz.browser.archive import PackageCopyingMixin
from lp.soyuz.browser.packagesearch import PackageSearchViewBase
+from lp.soyuz.interfaces.distributionjob import IPackageCopyJobSource
from lp.soyuz.interfaces.queue import IPackageUploadSet
+from lp.soyuz.model.queue import PackageUploadQueue
from lp.translations.browser.distroseries import (
check_distroseries_translations_viewable,
)
+# DistroSeries statuses that benefit from mass package upgrade support.
+UPGRADABLE_SERIES_STATUSES = [
+ SeriesStatus.FUTURE,
+ SeriesStatus.EXPERIMENTAL,
+ SeriesStatus.DEVELOPMENT,
+ ]
+
class DistroSeriesNavigation(GetitemNavigation, BugTargetTraversalMixin,
StructuralSubscriptionTargetTraversalMixin):
@@ -848,30 +857,28 @@
def cached_differences(self):
"""Return a batch navigator of filtered results."""
if self.specified_package_type == NON_BLACKLISTED:
- status=(
+ status = (
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,)
child_version_higher = False
elif self.specified_package_type == BLACKLISTED:
- status=(
+ status = (
DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
child_version_higher = False
elif self.specified_package_type == HIGHER_VERSION_THAN_PARENT:
- status=(
+ status = (
DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
child_version_higher = True
elif self.specified_package_type == RESOLVED:
- status=DistroSeriesDifferenceStatus.RESOLVED
+ status = DistroSeriesDifferenceStatus.RESOLVED
child_version_higher = False
else:
raise AssertionError('specified_package_type unknown')
differences = getUtility(
IDistroSeriesDifferenceSource).getForDistroSeries(
- self.context,
- difference_type = self.differences_type,
+ self.context, difference_type=self.differences_type,
source_package_name_filter=self.specified_name_filter,
- status=status,
- child_version_higher=child_version_higher)
+ status=status, child_version_higher=child_version_higher)
return BatchNavigator(differences, self.request)
@cachedproperty
@@ -889,7 +896,7 @@
differences = getUtility(
IDistroSeriesDifferenceSource).getForDistroSeries(
self.context,
- difference_type = self.differences_type,
+ difference_type=self.differences_type,
status=(
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT))
@@ -957,6 +964,56 @@
def sync_sources(self, action, data):
self._sync_sources(action, data)
+ def getUpgrades(self):
+ """Find straightforward package upgrades.
+
+ These are updates for packages that this distroseries shares
+ with a parent series, for which there have been updates in the
+ parent, and which do not have any changes in this series that
+ might complicate a sync.
+
+ :return: A result set of `DistroSeriesDifference`s.
+ """
+ return getUtility(IDistroSeriesDifferenceSource).getSimpleUpgrades(
+ self.context)
+
+ @action(_("Upgrade Packages"), name="upgrade", condition='canUpgrade')
+ def upgrade(self, action, data):
+ """Request synchronization of straightforward package upgrades."""
+ self.requestUpgrades()
+
+ def requestUpgrades(self):
+ """Request sync of packages that can be easily upgraded."""
+ target_distroseries = self.context
+ target_archive = target_distroseries.main_archive
+ differences_by_archive = (
+ getUtility(IDistroSeriesDifferenceSource)
+ .collateDifferencesByParentArchive(self.getUpgrades()))
+ for source_archive, differences in differences_by_archive.iteritems():
+ source_package_info = [
+ (difference.source_package_name.name,
+ difference.parent_source_version)
+ for difference in differences]
+ getUtility(IPackageCopyJobSource).create(
+ source_package_info, source_archive, target_archive,
+ target_distroseries, PackagePublishingPocket.UPDATES)
+ self.request.response.addInfoNotification(
+ (u"Upgrades of {context.displayname} packages have been "
+ u"requested. Please give Launchpad some time to complete "
+ u"these.").format(context=self.context))
+
+ def canUpgrade(self, action=None):
+ """Should the form offer a packages upgrade?"""
+ if self.context.status not in UPGRADABLE_SERIES_STATUSES:
+ # A feature freeze precludes blanket updates.
+ return False
+ elif self.getUpgrades().is_empty():
+ # There are no simple updates to perform.
+ return False
+ else:
+ queue = PackageUploadQueue(self.context, None)
+ return check_permission("launchpad.Edit", queue)
+
class DistroSeriesMissingPackagesView(DistroSeriesDifferenceBaseView,
LaunchpadFormView):
=== modified file 'lib/lp/registry/browser/tests/test_distroseries.py'
--- lib/lp/registry/browser/tests/test_distroseries.py 2011-05-08 13:41:34 +0000
+++ lib/lp/registry/browser/tests/test_distroseries.py 2011-05-09 09:57:34 +0000
@@ -20,6 +20,7 @@
from testtools.content_type import UTF8_TEXT
from testtools.matchers import (
EndsWith,
+ Equals,
LessThan,
Not,
)
@@ -32,6 +33,7 @@
from canonical.launchpad.testing.pages import find_tag_by_id
from canonical.launchpad.webapp.authorization import check_permission
from canonical.launchpad.webapp.batching import BatchNavigator
+from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel
from canonical.launchpad.webapp.publisher import canonical_url
from canonical.testing.layers import (
DatabaseFunctionalLayer,
@@ -49,6 +51,7 @@
DistroSeriesDifferenceType,
)
from lp.registry.interfaces.person import IPersonSet
+from lp.registry.interfaces.series import SeriesStatus
from lp.services.features import (
get_relevant_feature_controller,
getFeatureFlag,
@@ -64,12 +67,14 @@
from lp.soyuz.interfaces.component import IComponentSet
from lp.soyuz.interfaces.distributionjob import (
IInitialiseDistroSeriesJobSource,
+ IPackageCopyJobSource,
)
from lp.soyuz.interfaces.sourcepackageformat import (
ISourcePackageFormatSelectionSet,
)
from lp.soyuz.model.archivepermission import ArchivePermission
from lp.testing import (
+ anonymous_logged_in,
celebrity_logged_in,
feature_flags,
login_person,
@@ -77,6 +82,7 @@
set_feature_flag,
StormStatementRecorder,
TestCaseWithFactory,
+ with_celebrity_logged_in,
)
from lp.testing.matchers import HasQueryCount
from lp.testing.views import create_initialized_view
@@ -617,7 +623,30 @@
class TestDistroSeriesLocalDifferencesZopeless(TestCaseWithFactory):
"""Test the distroseries +localpackagediffs view."""
- layer = LaunchpadZopelessLayer
+ layer = LaunchpadFunctionalLayer
+
+ def makePackageUpgrade(self, derived_series=None):
+ """Create a `DistroSeriesDifference` for a package upgrade."""
+ base_version = '1.%d' % self.factory.getUniqueInteger()
+ versions = {
+ 'base': base_version,
+ 'parent': base_version + '-' + self.factory.getUniqueString(),
+ 'derived': base_version,
+ }
+ return self.factory.makeDistroSeriesDifference(
+ derived_series=derived_series, versions=versions,
+ set_base_version=True)
+
+ def makeDerivedSeries(self):
+ """Create a derived `DistroSeries`."""
+ return self.factory.makeDistroSeries(
+ parent_series=self.factory.makeDistroSeries())
+
+ def makeView(self, distroseries=None):
+ """Create a +localpackagediffs view for `distroseries`."""
+ if distroseries is None:
+ distroseries = self.makeDerivedSeries()
+ return create_initialized_view(distroseries, '+localpackagediffs')
def _create_child_and_parent(self):
parent_series = self.factory.makeDistroSeries(name='lucid')
@@ -633,8 +662,7 @@
self.assertIs(
None, getFeatureFlag('soyuz.derived-series-ui.enabled'))
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
response = view.request.response
self.assertEqual(302, response.getStatus())
@@ -645,8 +673,7 @@
# The view label includes the names of both series.
derived_series, parent_series = self._create_child_and_parent()
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
self.assertEqual(
"Source package differences between 'Derilucid' and "
@@ -663,8 +690,7 @@
derived_series=derived_series,
status=DistroSeriesDifferenceStatus.RESOLVED)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
self.assertContentEqual(
[current_difference], view.cached_differences.batch)
@@ -679,8 +705,7 @@
difference_type=(
DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
self.assertContentEqual(
[different_versions_diff], view.cached_differences.batch)
@@ -689,8 +714,7 @@
# The help link for popup help is included.
derived_series, parent_series = self._create_child_and_parent()
set_derived_series_ui_feature_flag(self)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
soup = BeautifulSoup(view())
help_links = soup.findAll(
@@ -702,12 +726,12 @@
derived_series, parent_series = self._create_child_and_parent()
difference = self.factory.makeDistroSeriesDifference(
derived_series=derived_series)
- difference.addComment(difference.owner, "Earlier comment")
- difference.addComment(difference.owner, "Latest comment")
+ with person_logged_in(derived_series.owner):
+ difference.addComment(difference.owner, "Earlier comment")
+ difference.addComment(difference.owner, "Latest comment")
set_derived_series_ui_feature_flag(self)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
# Find all the rows within the body of the table
# listing the differences.
@@ -726,8 +750,7 @@
derived_series=derived_series)
set_derived_series_ui_feature_flag(self)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
soup = BeautifulSoup(view())
diff_table = soup.find('table', {'class': 'listing'})
row = diff_table.tbody.findAll('tr')[0]
@@ -763,8 +786,7 @@
version=new_version)
set_derived_series_ui_feature_flag(self)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
soup = BeautifulSoup(view())
diff_table = soup.find('table', {'class': 'listing'})
row = diff_table.tbody.tr
@@ -799,14 +821,16 @@
derived_series=derived_series)
# Delete the publications.
- difference.source_pub.status = PackagePublishingStatus.DELETED
- difference.parent_source_pub.status = PackagePublishingStatus.DELETED
+ with celebrity_logged_in("admin"):
+ difference.source_pub.status = (
+ PackagePublishingStatus.DELETED)
+ difference.parent_source_pub.status = (
+ PackagePublishingStatus.DELETED)
# Flush out the changes and invalidate caches (esp. property caches).
flush_database_caches()
set_derived_series_ui_feature_flag(self)
- view = create_initialized_view(
- derived_series, '+localpackagediffs')
+ view = self.makeView(derived_series)
soup = BeautifulSoup(view())
diff_table = soup.find('table', {'class': 'listing'})
row = diff_table.tbody.tr
@@ -823,6 +847,111 @@
self.assertEqual(versions['derived'], derived_span[0].string.strip())
self.assertEqual(versions['parent'], parent_span[0].string.strip())
+ def test_getUpgrades_shows_updates_in_parent(self):
+ # The view's getUpgrades methods lists packages that can be
+ # trivially upgraded: changed in the parent, not changed in the
+ # derived series, but present in both.
+ dsd = self.makePackageUpgrade()
+ view = self.makeView(dsd.derived_series)
+ self.assertContentEqual([dsd], view.getUpgrades())
+
+ def test_upgrades_are_offered_if_appropriate(self):
+ # The"Upgrade Packages" button will only be shown to privileged users.
+ dsd = self.makePackageUpgrade()
+ view = self.makeView(dsd.derived_series)
+ with celebrity_logged_in("admin"):
+ self.assertTrue(view.canUpgrade())
+ with person_logged_in(self.factory.makePerson()):
+ self.assertFalse(view.canUpgrade())
+ with anonymous_logged_in():
+ self.assertFalse(view.canUpgrade())
+
+ @with_celebrity_logged_in("admin")
+ def test_upgrades_offered_only_if_available(self):
+ # If there are no upgrades, the "Upgrade Packages" button won't
+ # be shown.
+ view = self.makeView()
+ self.assertFalse(view.canUpgrade())
+ self.makePackageUpgrade()
+ self.assertTrue(view.canUpgrade())
+
+ @with_celebrity_logged_in("admin")
+ def test_upgrades_not_offered_after_feature_freeze(self):
+ # There won't be an "Upgrade Packages" button once feature
+ # freeze has occurred. Mass updates would not make sense after
+ # that point.
+ upgradeable = {}
+ for status in SeriesStatus.items:
+ dsd = self.makePackageUpgrade()
+ dsd.derived_series.status = status
+ view = self.makeView(dsd.derived_series)
+ upgradeable[status] = view.canUpgrade()
+ expected = {
+ SeriesStatus.FUTURE: True,
+ SeriesStatus.EXPERIMENTAL: True,
+ SeriesStatus.DEVELOPMENT: True,
+ SeriesStatus.FROZEN: False,
+ SeriesStatus.CURRENT: False,
+ SeriesStatus.SUPPORTED: False,
+ SeriesStatus.OBSOLETE: False,
+ }
+ self.assertEqual(expected, upgradeable)
+
+ def test_upgrade_creates_sync_jobs(self):
+ # requestUpgrades generates PackageCopyJobs for the upgrades
+ # that need doing.
+ dsd = self.makePackageUpgrade()
+ series = dsd.derived_series
+ view = self.makeView(series)
+ view.requestUpgrades()
+ job_source = getUtility(IPackageCopyJobSource)
+ jobs = list(
+ job_source.getActiveJobs(series.distribution.main_archive))
+ self.assertEquals(1, len(jobs))
+ job = jobs[0]
+ self.assertEquals(series, job.distroseries)
+ source_package_info = list(job.source_packages)
+ self.assertEquals(1, len(source_package_info))
+ self.assertEqual(
+ (dsd.source_package_name.name, dsd.parent_source_version),
+ source_package_info[0][:2])
+
+ def test_upgrade_gives_feedback(self):
+ # requestUpgrades doesn't instantly perform package upgrades,
+ # but it shows the user a notice that the upgrades have been
+ # requested.
+ dsd = self.makePackageUpgrade()
+ view = self.makeView(dsd.derived_series)
+ view.requestUpgrades()
+ expected = {
+ "level": BrowserNotificationLevel.INFO,
+ "message":
+ ("Upgrades of {0.displayname} packages have been "
+ "requested. Please give Launchpad some time to "
+ "complete these.").format(dsd.derived_series),
+ }
+ observed = map(vars, view.request.response.notifications)
+ self.assertEqual([expected], observed)
+
+ def test_requestUpgrade_is_efficient(self):
+ # A single web request may need to schedule large numbers of
+ # package upgrades. It must do so without issuing large numbers
+ # of database queries.
+ derived_series, parent_series = self._create_child_and_parent()
+ # Take a baseline measure of queries.
+ self.makePackageUpgrade(derived_series=derived_series)
+ flush_database_caches()
+ with StormStatementRecorder() as recorder1:
+ self.makeView(derived_series).requestUpgrades()
+ self.assertThat(recorder1, HasQueryCount(LessThan(10)))
+ # The query count does not increase with more differences.
+ for index in xrange(3):
+ self.makePackageUpgrade(derived_series=derived_series)
+ flush_database_caches()
+ with StormStatementRecorder() as recorder2:
+ self.makeView(derived_series).requestUpgrades()
+ self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
+
class TestDistroSeriesLocalDifferencesFunctional(TestCaseWithFactory):
=== modified file 'lib/lp/registry/interfaces/distroseriesdifference.py'
--- lib/lp/registry/interfaces/distroseriesdifference.py 2011-04-27 15:12:08 +0000
+++ lib/lp/registry/interfaces/distroseriesdifference.py 2011-05-09 09:57:34 +0000
@@ -195,7 +195,7 @@
"""
latest_comment = Reference(
- Interface, # IDistroSeriesDifferenceComment
+ Interface, # IDistroSeriesDifferenceComment
title=_("The latest comment"),
readonly=True)
@@ -317,3 +317,24 @@
:param source_package_name: The name of the package difference.
:type source_package_name: unicode.
"""
+
+ def getSimpleUpgrades(distro_series):
+ """Find pending upgrades that can be performed mindlessly.
+
+ These are `DistroSeriesDifferences` where the parent has been
+ updated and the child still has the old version, unchanged.
+
+ Blacklisted items are excluded.
+ """
+
+ def collateDifferencesByParentArchive(differences):
+ """Collate the given differences by parent archive.
+
+ The given `IDistroSeriesDifference`s are returned in a `dict`, with
+ the parent `Archive` as keys.
+
+ :param differences: An iterable sequence of `IDistroSeriesDifference`.
+
+ :return: A `dict` of iterable sequences of `IDistroSeriesDifference`
+ keyed by their parent `IArchive`.
+ """
=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py 2011-05-06 11:18:35 +0000
+++ lib/lp/registry/model/distroseriesdifference.py 2011-05-09 09:57:34 +0000
@@ -265,7 +265,7 @@
parent_series=None):
"""See `IDistroSeriesDifferenceSource`."""
if difference_type is None:
- difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS
+ difference_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
if status is None:
status = (
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
@@ -394,6 +394,38 @@
SourcePackageName.id),
SourcePackageName.name == source_package_name).one()
+ @staticmethod
+ def getSimpleUpgrades(distro_series):
+ """See `IDistroSeriesDifferenceSource`.
+
+ Eager-load related `ISourcePackageName` records.
+ """
+ differences = IStore(DistroSeriesDifference).find(
+ (DistroSeriesDifference, SourcePackageName),
+ DistroSeriesDifference.derived_series == distro_series,
+ DistroSeriesDifference.difference_type ==
+ DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
+ DistroSeriesDifference.status ==
+ DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
+ DistroSeriesDifference.parent_source_version !=
+ DistroSeriesDifference.base_version,
+ DistroSeriesDifference.source_version ==
+ DistroSeriesDifference.base_version,
+ SourcePackageName.id ==
+ DistroSeriesDifference.source_package_name_id)
+ return DecoratedResultSet(differences, itemgetter(0))
+
+ @staticmethod
+ def collateDifferencesByParentArchive(differences):
+ by_archive = dict()
+ for difference in differences:
+ archive = difference.parent_series.main_archive
+ if archive in by_archive:
+ by_archive[archive].append(difference)
+ else:
+ by_archive[archive] = [difference]
+ return by_archive
+
@cachedproperty
def source_pub(self):
"""See `IDistroSeriesDifference`."""
=== modified file 'lib/lp/registry/tests/test_distroseriesdifference.py'
--- lib/lp/registry/tests/test_distroseriesdifference.py 2011-05-06 11:18:35 +0000
+++ lib/lp/registry/tests/test_distroseriesdifference.py 2011-05-09 09:57:34 +0000
@@ -149,7 +149,7 @@
'parent': '1.0',
'derived': '0.9',
})
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -170,7 +170,7 @@
versions=['1.0', '1.2'])
parent_changelog = self.factory.makeChangelog(
versions=['1.0', '1.3'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(versions={
'derived': '1.2',
'parent': '1.3',
@@ -189,12 +189,12 @@
# Resolve the DSD by making the same package version published
# in parent and derived.
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
version='1.4')
- new_parent_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.parent_series,
status=PackagePublishingStatus.PENDING,
@@ -218,7 +218,7 @@
'derived': '1.0',
},
status=DistroSeriesDifferenceStatus.RESOLVED)
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -240,7 +240,7 @@
'parent': '1.0',
'derived': '0.9',
})
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -266,7 +266,7 @@
},
difference_type=(
DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
- new_parent_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.parent_series,
status=PackagePublishingStatus.PENDING,
@@ -290,7 +290,7 @@
difference_type=(
DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES),
status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -315,7 +315,7 @@
'parent': '1.0',
},
status=DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS)
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -332,7 +332,7 @@
# The title is a friendly description of the difference.
parent_series = self.factory.makeDistroSeries(name="lucid")
derived_series = self.factory.makeDistroSeries(name="derilucid")
- dsp = self.factory.makeDistroSeriesParent(
+ self.factory.makeDistroSeriesParent(
derived_series=derived_series, parent_series=parent_series)
ds_diff = self.factory.makeDistroSeriesDifference(
source_package_name_str="foonew", derived_series=derived_series,
@@ -395,8 +395,7 @@
person = self.factory.makePerson()
with person_logged_in(person):
self.assertTrue(check_permission('launchpad.Edit', ds_diff))
- diff_comment = ds_diff.addComment(
- ds_diff.derived_series.owner, "Boo")
+ ds_diff.addComment(ds_diff.derived_series.owner, "Boo")
def _setupPackageSets(self, ds_diff, distroseries, nb_packagesets):
# Helper method to create packages sets.
@@ -470,7 +469,7 @@
'parent': '1.0',
},
status=DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS)
- new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+ self.factory.makeSourcePackagePublishingHistory(
sourcepackagename=ds_diff.source_package_name,
distroseries=ds_diff.derived_series,
status=PackagePublishingStatus.PENDING,
@@ -527,11 +526,11 @@
def test_base_version_multiple(self):
# The latest common base version is set as the base-version.
dsp = self.factory.makeDistroSeriesParent()
- source_package_name = self.factory.getOrMakeSourcePackageName('foo')
+ self.factory.getOrMakeSourcePackageName('foo')
# Create changelogs for both.
changelog_lfa = self.factory.makeChangelog('foo', ['1.2', '1.1'])
parent_changelog_lfa = self.factory.makeChangelog('foo', ['1.1'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(
derived_series=dsp.derived_series, source_package_name_str='foo',
@@ -549,13 +548,13 @@
# If the maximum base version is invalid, it is discarded and not
# set as the base version.
dsp = self.factory.makeDistroSeriesParent()
- source_package_name = self.factory.getOrMakeSourcePackageName('foo')
+ self.factory.getOrMakeSourcePackageName('foo')
# Create changelogs for both.
changelog_lfa = self.factory.makeChangelog(
'foo', ['1:2.0-1', 'a1:1.8.8-070403-1~priv1', '1:1.7-1'])
parent_changelog_lfa = self.factory.makeChangelog(
'foo', ['1:2.0-2', 'a1:1.8.8-070403-1~priv1', '1:1.7-1'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(
derived_series=dsp.derived_series, source_package_name_str='foo',
@@ -583,7 +582,7 @@
versions=['1.0', '1.2'])
parent_changelog = self.factory.makeChangelog(
versions=['1.0', '1.3'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(versions={
'derived': '1.2',
@@ -606,7 +605,7 @@
versions=['1.0', '1.2'])
parent_changelog = self.factory.makeChangelog(
versions=['1.0', '1.3'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(versions={
'derived': '1.2',
@@ -626,7 +625,7 @@
versions=['1.0', '1.2'])
parent_changelog = self.factory.makeChangelog(
versions=['1.0', '1.3'])
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(
versions={
@@ -656,12 +655,12 @@
# {derived,parent}_versions must be ordered (e.g. ['1.1',
# '1.2', '1.3']).
if status is None:
- status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION
+ status = DistroSeriesDifferenceStatus.NEEDS_ATTENTION
derived_changelog = self.factory.makeChangelog(
versions=derived_versions)
parent_changelog = self.factory.makeChangelog(
versions=parent_versions)
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
ds_diff = self.factory.makeDistroSeriesDifference(
status=status,
versions={
@@ -773,7 +772,7 @@
def createPublication(self, spn, versions, distroseries,
status=PackagePublishingStatus.PUBLISHED):
changelog_lfa = self.factory.makeChangelog(spn.name, versions)
- transaction.commit() # Yay, librarian.
+ transaction.commit() # Yay, librarian.
spr = self.factory.makeSourcePackageRelease(
sourcepackagename=spn, version=versions[0],
changelog=changelog_lfa)
@@ -787,7 +786,7 @@
dsp = self.factory.makeDistroSeriesParent()
spn = self.factory.getOrMakeSourcePackageName(
name=self.factory.getUniqueString())
- parent_spph = self.createPublication(
+ self.createPublication(
spn, ['1.2-1', '1.0-1'], dsp.parent_series)
spph = self.createPublication(
spn, ['1.1-1', '1.0-1'], dsp.derived_series)
@@ -900,6 +899,34 @@
derived_series=derived_series)
return dsp.derived_series
+ def makeVersionDifference(self, derived_series=None, changed_parent=False,
+ changed_child=False, status=None):
+ """Create a `DistroSeriesDifference` between package versions.
+
+ The differing package will exist in both the parent series and in the
+ child.
+
+ :param derived_series: Optional `DistroSeries` that the difference is
+ for. If not given, one will be created.
+ :param changed_parent: Whether the difference should show a change in
+ the parent's version of the package.
+ :param changed_child: Whether the difference should show a change in
+ the child's version of the package.
+ :param status: Optional status for the `DistroSeriesDifference`. If
+ not given, defaults to `NEEDS_ATTENTION`.
+ """
+ if status is None:
+ status = DistroSeriesDifferenceStatus.NEEDS_ATTENTION
+ base_version = "1.%d" % self.factory.getUniqueInteger()
+ versions = dict.fromkeys(('base', 'parent', 'derived'), base_version)
+ if changed_parent:
+ versions['parent'] += "-%s" % self.factory.getUniqueString()
+ if changed_child:
+ versions['derived'] += "-%s" % self.factory.getUniqueString()
+ return self.factory.makeDistroSeriesDifference(
+ derived_series=derived_series, versions=versions, status=status,
+ set_base_version=True)
+
def test_getForDistroSeries_default(self):
# By default all differences needing attention for the given
# series are returned.
@@ -915,7 +942,7 @@
def test_getForDistroSeries_filters_by_distroseries(self):
# Differences for other series are not included.
derived_series = self.makeDerivedSeries()
- diffs = self.makeDiffsForDistroSeries(derived_series)
+ self.makeDiffsForDistroSeries(derived_series)
diff_for_other_series = self.factory.makeDistroSeriesDifference()
result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
@@ -926,9 +953,9 @@
def test_getForDistroSeries_filters_by_type(self):
# Only differences for the specified types are returned.
derived_series = self.makeDerivedSeries()
- diffs = self.makeDiffsForDistroSeries(derived_series)
+ self.makeDiffsForDistroSeries(derived_series)
- result = getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
+ getUtility(IDistroSeriesDifferenceSource).getForDistroSeries(
derived_series,
DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)
@@ -1011,6 +1038,79 @@
self.assertEqual(ds_diff, result)
+ def test_getSimpleUpgrades_finds_simple_update(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ dsd = self.makeVersionDifference(changed_parent=True)
+ self.assertEqual(dsd.base_version, dsd.source_version)
+ self.assertContentEqual(
+ [dsd], dsd_source.getSimpleUpgrades(dsd.derived_series))
+
+ def test_getSimpleUpgrades_ignores_hidden_differences(self):
+ invisible_statuses = [
+ DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
+ DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS,
+ DistroSeriesDifferenceStatus.RESOLVED,
+ ]
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ series = self.makeDerivedSeries()
+ for status in invisible_statuses:
+ self.makeVersionDifference(
+ derived_series=series, changed_parent=True, status=status)
+ self.assertContentEqual([], dsd_source.getSimpleUpgrades(series))
+
+ def test_getSimpleUpgrades_ignores_other_distroseries(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ self.makeVersionDifference(changed_parent=True)
+ self.assertContentEqual(
+ [], dsd_source.getSimpleUpgrades(self.factory.makeDistroSeries()))
+
+ def test_getSimpleUpgrades_ignores_packages_changed_in_child(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ dsd = self.makeVersionDifference(
+ changed_parent=True, changed_child=True)
+ self.assertContentEqual(
+ [], dsd_source.getSimpleUpgrades(dsd.derived_series))
+
+ def test_getSimpleUpgrades_ignores_packages_not_updated_in_parent(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ dsd = self.makeVersionDifference(changed_parent=False)
+ self.assertContentEqual(
+ [], dsd_source.getSimpleUpgrades(dsd.derived_series))
+
+ def test_getSimpleUpgrades_ignores_packages_unique_to_child(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
+ dsd = self.factory.makeDistroSeriesDifference(
+ difference_type=diff_type)
+ self.assertContentEqual(
+ [], dsd_source.getSimpleUpgrades(dsd.derived_series))
+
+ def test_getSimpleUpgrades_ignores_packages_missing_from_child(self):
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
+ dsd = self.factory.makeDistroSeriesDifference(
+ difference_type=diff_type)
+ self.assertContentEqual(
+ [], dsd_source.getSimpleUpgrades(dsd.derived_series))
+
+ def test_collateDifferencesByParentArchive(self):
+ dsp1 = self.factory.makeDistroSeriesParent()
+ dsp2 = self.factory.makeDistroSeriesParent()
+ differences = [
+ self.factory.makeDistroSeriesDifference(dsp1.derived_series),
+ self.factory.makeDistroSeriesDifference(dsp2.derived_series),
+ self.factory.makeDistroSeriesDifference(dsp1.derived_series),
+ self.factory.makeDistroSeriesDifference(dsp2.derived_series),
+ ]
+ dsd_source = getUtility(IDistroSeriesDifferenceSource)
+ observed = (
+ dsd_source.collateDifferencesByParentArchive(differences))
+ expected = {
+ dsp1.parent_series.main_archive: differences[0::2],
+ dsp2.parent_series.main_archive: differences[1::2],
+ }
+ self.assertEqual(observed, expected)
+
class TestMostRecentComments(TestCaseWithFactory):