~launchpad-pqm/launchpad/devel

12494.1.17 by Gavin Panella
Resume work on +initseries.
1
# Copyright 2011 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
"""Tests for `lp.registry.browser.distroseries`."""
5
6
__metaclass__ = type
7
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
8
from datetime import timedelta
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
9
import difflib
12742.3.25 by Gavin Panella
Fix lint and imports.
10
import re
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
11
from textwrap import TextWrapper
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
12
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
13
from BeautifulSoup import BeautifulSoup
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
14
from lazr.restful.interfaces import IJSONRequestCache
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
15
from lxml import html
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
16
import soupmatchers
17
from storm.zope.interfaces import IResultSet
12742.3.38 by Gavin Panella
Add statement counts for 0, 2, and 4 difference rows.
18
from testtools.content import (
19
    Content,
20
    text_content,
21
    )
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
22
from testtools.content_type import UTF8_TEXT
12742.3.2 by Gavin Panella
First attempt at preloading source_pub. Not very successful so far.
23
from testtools.matchers import (
24
    EndsWith,
12959.2.21 by Gavin Panella
Test to demonstrate that requestUpgrade() is efficient with the database.
25
    Equals,
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
26
    LessThan,
12792.7.9 by Raphael Badin
Merge devel.
27
    Not,
12742.3.2 by Gavin Panella
First attempt at preloading source_pub. Not very successful so far.
28
    )
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
29
from zope.component import getUtility
12742.3.10 by Gavin Panella
Keep track of all queries.
30
from zope.security.proxy import removeSecurityProxy
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
31
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
32
from canonical.config import config
13013.1.1 by Raphael Badin
Fix series status check.
33
from canonical.database.constants import UTC_NOW
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
34
from canonical.database.sqlbase import flush_database_caches
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
35
from canonical.launchpad.testing.pages import (
36
    extract_text,
37
    find_tag_by_id,
38
    )
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
39
from canonical.launchpad.webapp.authorization import check_permission
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
40
from canonical.launchpad.webapp.batching import BatchNavigator
12959.2.28 by Gavin Panella
Fix last few test failures, phew.
41
from canonical.launchpad.webapp.interaction import get_current_principal
12959.2.18 by Gavin Panella
Some attempts to fix up test errors/failures.
42
from canonical.launchpad.webapp.interfaces import BrowserNotificationLevel
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
43
from canonical.launchpad.webapp.publisher import canonical_url
44
from canonical.testing.layers import (
45
    DatabaseFunctionalLayer,
46
    LaunchpadFunctionalLayer,
47
    LaunchpadZopelessLayer,
48
    )
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
49
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
7675.1154.2 by Jeroen Vermeulen
DSD source versions are stored as raw strings, so need to cast to Version first.
50
from lp.archivepublisher.debversion import Version
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
51
from lp.registry.browser.distroseries import (
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
52
    HIGHER_VERSION_THAN_PARENT,
13543.2.1 by Raphael Badin
Add a new 'All packages' option to the filtering form on +localpackagediffs.
53
    ALL,
13063.3.4 by Julian Edwards
fix remaining tests
54
    NON_IGNORED,
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
55
    RESOLVED,
13168.14.5 by Raphael Badin
Refactor test_not_is_first_derivation tests.
56
    seriesToVocab,
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
57
    )
58
from lp.registry.enum import (
59
    DistroSeriesDifferenceStatus,
60
    DistroSeriesDifferenceType,
61
    )
13013.1.1 by Raphael Badin
Fix series status check.
62
from lp.registry.interfaces.pocket import PackagePublishingPocket
12959.2.20 by Gavin Panella
Remove unnecessary test.
63
from lp.registry.interfaces.series import SeriesStatus
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
64
from lp.services.features import (
12792.7.9 by Raphael Badin
Merge devel.
65
    get_relevant_feature_controller,
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
66
    getFeatureFlag,
67
    )
12959.2.28 by Gavin Panella
Fix last few test failures, phew.
68
from lp.services.features.testing import FeatureFixture
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
69
from lp.services.utils import utc_now
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
70
from lp.soyuz.enums import (
12981.1.2 by Raphael Badin
Roll back 12977.
71
    ArchivePermissionType,
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
72
    PackagePublishingStatus,
73
    SourcePackageFormat,
74
    )
12981.1.2 by Raphael Badin
Roll back 12977.
75
from lp.soyuz.interfaces.component import IComponentSet
12792.7.9 by Raphael Badin
Merge devel.
76
from lp.soyuz.interfaces.distributionjob import (
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
77
    IDistroSeriesDifferenceJobSource,
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
78
    IInitializeDistroSeriesJobSource,
7675.1131.1 by William Grant
Fix incompatibilities between the changes in r10537 and r10538.
79
    )
7675.1137.3 by Raphael Badin
Cache packagesets.
80
from lp.soyuz.interfaces.packagecopyjob import IPlainPackageCopyJobSource
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
81
from lp.soyuz.interfaces.sourcepackageformat import (
82
    ISourcePackageFormatSelectionSet,
83
    )
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
84
from lp.soyuz.model import distroseriesdifferencejob
12981.1.2 by Raphael Badin
Roll back 12977.
85
from lp.soyuz.model.archivepermission import ArchivePermission
13181.1.1 by Julian Edwards
make syncs always create a PCJ
86
from lp.soyuz.model.packagecopyjob import PlainPackageCopyJob
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
87
from lp.testing import (
13261.4.2 by Raphael Badin
Add a test for anonymous access.
88
    ANONYMOUS,
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
89
    anonymous_logged_in,
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
90
    celebrity_logged_in,
13261.4.2 by Raphael Badin
Add a test for anonymous access.
91
    login,
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
92
    login_celebrity,
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
93
    login_person,
13444.6.34 by Gavin Panella
Show the uploader too.
94
    normalize_whitespace,
12736.4.6 by Gavin Panella
Demonstrate how the DistroSeries created in +addseries is initialized.
95
    person_logged_in,
12742.3.10 by Gavin Panella
Keep track of all queries.
96
    StormStatementRecorder,
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
97
    TestCaseWithFactory,
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
98
    with_celebrity_logged_in,
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
99
    )
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
100
from lp.testing.fakemethod import FakeMethod
13168.8.9 by Gavin Panella
Format imports.
101
from lp.testing.matchers import (
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
102
    DocTestMatches,
13168.8.9 by Gavin Panella
Format imports.
103
    EqualsIgnoringWhitespace,
104
    HasQueryCount,
105
    )
12494.1.17 by Gavin Panella
Resume work on +initseries.
106
from lp.testing.views import create_initialized_view
107
108
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
109
def set_derived_series_ui_feature_flag(test_case):
12998.1.1 by mbp at canonical
Remove ad-hoc reimplementation of test feature fixture
110
    test_case.useFixture(FeatureFixture({
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
111
        u'soyuz.derived_series_ui.enabled': u'on',
12998.1.1 by mbp at canonical
Remove ad-hoc reimplementation of test feature fixture
112
        }))
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
113
13001.1.2 by Julian Edwards
fix lint
114
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
115
def set_derived_series_sync_feature_flag(test_case):
116
    test_case.useFixture(FeatureFixture({
13001.1.3 by Julian Edwards
rename the flag
117
        u'soyuz.derived_series_sync.enabled': u'on',
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
118
        u'soyuz.derived_series_ui.enabled': u'on',
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
119
        }))
120
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
121
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
122
def set_derived_series_difference_jobs_feature_flag(test_case):
123
    test_case.useFixture(FeatureFixture({
124
        distroseriesdifferencejob.FEATURE_FLAG_ENABLE_MODULE: u'on',
125
        }))
126
127
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
128
class TestDistroSeriesView(TestCaseWithFactory):
129
    """Test the distroseries +index view."""
130
131
    layer = LaunchpadZopelessLayer
132
133
    def test_needs_linking(self):
134
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
135
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
136
        view = create_initialized_view(distroseries, '+index')
137
        self.assertEqual(view.needs_linking, None)
138
12792.7.9 by Raphael Badin
Merge devel.
139
    def _createDifferenceAndGetView(self, difference_type):
140
        # Helper function to create a valid DSD.
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
141
        dsp = self.factory.makeDistroSeriesParent()
12959.2.2 by Jeroen Vermeulen
More lint.
142
        self.factory.makeDistroSeriesDifference(
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
143
            derived_series=dsp.derived_series,
144
            difference_type=difference_type)
145
        return create_initialized_view(dsp.derived_series, '+index')
12792.7.9 by Raphael Badin
Merge devel.
146
147
    def test_num_differences(self):
148
        diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
149
        view = self._createDifferenceAndGetView(diff_type)
150
        self.assertEqual(1, view.num_differences)
151
152
    def test_num_differences_in_parent(self):
153
        diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
154
        view = self._createDifferenceAndGetView(diff_type)
155
        self.assertEqual(1, view.num_differences_in_parent)
156
157
    def test_num_differences_in_child(self):
158
        diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
159
        view = self._createDifferenceAndGetView(diff_type)
160
        self.assertEqual(1, view.num_differences_in_child)
161
162
163
class DistroSeriesIndexFunctionalTestCase(TestCaseWithFactory):
164
    """Test the distroseries +index page."""
165
166
    layer = DatabaseFunctionalLayer
167
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
168
    def _setupDifferences(self, name, parent_names, nb_diff_versions,
12792.7.9 by Raphael Badin
Merge devel.
169
                          nb_diff_child, nb_diff_parent):
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
170
        # Helper to create DSDs of the different types.
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
171
        derived_series = self.factory.makeDistroSeries(name=name)
12792.7.9 by Raphael Badin
Merge devel.
172
        self.simple_user = self.factory.makePerson()
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
173
        # parent_names can be a list of parent names or a single name
174
        # for a single parent (e.g. ['parent1_name', 'parent2_name'] or
175
        # 'parent_name').
176
        # If multiple parents are created, the DSDs will be created with
177
        # the first one.
178
        if type(parent_names) == str:
179
            parent_names = [parent_names]
180
        dsps = []
181
        for parent_name in parent_names:
182
            parent_series = self.factory.makeDistroSeries(name=parent_name)
183
            dsps.append(self.factory.makeDistroSeriesParent(
184
                derived_series=derived_series, parent_series=parent_series))
185
        first_parent_series = dsps[0].parent_series
12792.7.9 by Raphael Badin
Merge devel.
186
        for i in range(nb_diff_versions):
187
            diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
188
            self.factory.makeDistroSeriesDifference(
189
                derived_series=derived_series,
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
190
                difference_type=diff_type,
191
                parent_series=first_parent_series)
12792.7.9 by Raphael Badin
Merge devel.
192
        for i in range(nb_diff_child):
193
            diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
194
            self.factory.makeDistroSeriesDifference(
195
                derived_series=derived_series,
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
196
                difference_type=diff_type,
197
                parent_series=first_parent_series)
12792.7.9 by Raphael Badin
Merge devel.
198
        for i in range(nb_diff_parent):
199
            diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
200
            self.factory.makeDistroSeriesDifference(
201
                derived_series=derived_series,
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
202
                difference_type=diff_type,
203
                parent_series=first_parent_series)
12792.7.9 by Raphael Badin
Merge devel.
204
        return derived_series
205
206
    def test_differences_no_flag_no_portlet(self):
207
        # The portlet is not displayed if the feature flag is not enabled.
208
        derived_series = self._setupDifferences('deri', 'sid', 1, 2, 2)
209
        portlet_header = soupmatchers.HTMLContains(
210
            soupmatchers.Tag(
211
                'Derivation portlet header', 'h2',
212
                text='Derived from Sid'),
213
            )
214
215
        with person_logged_in(self.simple_user):
216
            view = create_initialized_view(
217
                derived_series,
218
                '+index',
219
                principal=self.simple_user)
12981.1.2 by Raphael Badin
Roll back 12977.
220
            html_content = view()
12792.7.9 by Raphael Badin
Merge devel.
221
222
        self.assertEqual(
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
223
            None, getFeatureFlag('soyuz.derived_series_ui.enabled'))
12981.1.2 by Raphael Badin
Roll back 12977.
224
        self.assertThat(html_content, Not(portlet_header))
12792.7.9 by Raphael Badin
Merge devel.
225
226
    def test_differences_portlet_all_differences(self):
227
        # The difference portlet shows the differences with the parent
228
        # series.
229
        set_derived_series_ui_feature_flag(self)
230
        derived_series = self._setupDifferences('deri', 'sid', 1, 2, 3)
231
        portlet_display = soupmatchers.HTMLContains(
232
            soupmatchers.Tag(
233
                'Derivation portlet header', 'h2',
234
                text='Derived from Sid'),
235
            soupmatchers.Tag(
236
                'Differences link', 'a',
12881.4.10 by Raphael Badin
Testfix.
237
                text=re.compile('\s*1 package with differences\s*'),
12792.7.9 by Raphael Badin
Merge devel.
238
                attrs={'href': re.compile('.*/\+localpackagediffs')}),
239
            soupmatchers.Tag(
240
                'Parent diffs link', 'a',
12881.4.10 by Raphael Badin
Testfix.
241
                text=re.compile('\s*2 packages only in Sid\s*'),
12792.7.9 by Raphael Badin
Merge devel.
242
                attrs={'href': re.compile('.*/\+missingpackages')}),
243
            soupmatchers.Tag(
244
                'Child diffs link', 'a',
12881.4.10 by Raphael Badin
Testfix.
245
                text=re.compile('\s*3 packages only in Deri\s*'),
12792.7.9 by Raphael Badin
Merge devel.
246
                attrs={'href': re.compile('.*/\+uniquepackages')}))
247
248
        with person_logged_in(self.simple_user):
249
            view = create_initialized_view(
250
                derived_series,
251
                '+index',
252
                principal=self.simple_user)
253
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
254
            # self.features to NullFeatureController.
255
            view.request.features = get_relevant_feature_controller()
12981.1.2 by Raphael Badin
Roll back 12977.
256
            html_content = view()
12792.7.9 by Raphael Badin
Merge devel.
257
12981.1.2 by Raphael Badin
Roll back 12977.
258
        self.assertThat(html_content, portlet_display)
12792.7.9 by Raphael Badin
Merge devel.
259
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
260
    def test_differences_portlet_all_differences_multiple_parents(self):
261
        # The difference portlet shows the differences with the multiple
262
        # parent series.
263
        set_derived_series_ui_feature_flag(self)
264
        derived_series = self._setupDifferences(
265
            'deri', ['sid1', 'sid2'], 0, 1, 0)
266
        portlet_display = soupmatchers.HTMLContains(
267
            soupmatchers.Tag(
268
                'Derivation portlet header', 'h2',
269
                text='Derived from 2 parents'),
270
            soupmatchers.Tag(
271
                'Parent diffs link', 'a',
272
                text=re.compile('\s*1 package only in a parent series\s*'),
273
                attrs={'href': re.compile('.*/\+missingpackages')}))
274
275
        with person_logged_in(self.simple_user):
276
            view = create_initialized_view(
277
                derived_series,
278
                '+index',
279
                principal=self.simple_user)
280
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
281
            # self.features to NullFeatureController.
282
            view.request.features = get_relevant_feature_controller()
283
            html_text = view()
284
285
        self.assertThat(html_text, portlet_display)
286
12792.7.9 by Raphael Badin
Merge devel.
287
    def test_differences_portlet_no_differences(self):
288
        # The difference portlet displays 'No differences' if there is no
289
        # differences with the parent.
290
        set_derived_series_ui_feature_flag(self)
291
        derived_series = self._setupDifferences('deri', 'sid', 0, 0, 0)
292
        portlet_display = soupmatchers.HTMLContains(
293
            soupmatchers.Tag(
294
                'Derivation portlet header', 'h2',
295
                text='Derived from Sid'),
296
            soupmatchers.Tag(
297
                'Child diffs link', True,
298
                text=re.compile('\s*No differences\s*')),
299
              )
300
301
        with person_logged_in(self.simple_user):
302
            view = create_initialized_view(
303
                derived_series,
304
                '+index',
305
                principal=self.simple_user)
306
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
307
            # self.features to NullFeatureController.
308
            view.request.features = get_relevant_feature_controller()
12981.1.2 by Raphael Badin
Roll back 12977.
309
            html_content = view()
12792.7.9 by Raphael Badin
Merge devel.
310
12981.1.2 by Raphael Badin
Roll back 12977.
311
        self.assertThat(html_content, portlet_display)
12792.7.9 by Raphael Badin
Merge devel.
312
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
313
    def test_differences_portlet_initializing(self):
314
        # The difference portlet displays 'The series is initializing.' if
315
        # there is an initializing job for the series.
12792.7.9 by Raphael Badin
Merge devel.
316
        set_derived_series_ui_feature_flag(self)
13117.2.10 by Raphael Badin
Fix tests.
317
        derived_series = self.factory.makeDistroSeries()
318
        parent_series = self.factory.makeDistroSeries()
319
        self.simple_user = self.factory.makePerson()
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
320
        job_source = getUtility(IInitializeDistroSeriesJobSource)
13117.2.10 by Raphael Badin
Fix tests.
321
        job_source.create(derived_series, [parent_series.id])
12792.7.9 by Raphael Badin
Merge devel.
322
        portlet_display = soupmatchers.HTMLContains(
323
            soupmatchers.Tag(
324
                'Derived series', 'h2',
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
325
                text='Series initialization in progress'),
12792.7.9 by Raphael Badin
Merge devel.
326
            soupmatchers.Tag(
327
                'Init message', True,
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
328
                text=re.compile('\s*This series is initializing.\s*')),
12792.7.9 by Raphael Badin
Merge devel.
329
              )
330
331
        with person_logged_in(self.simple_user):
332
            view = create_initialized_view(
333
                derived_series,
334
                '+index',
335
                principal=self.simple_user)
336
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
337
            # self.features to NullFeatureController.
338
            view.request.features = get_relevant_feature_controller()
12981.1.2 by Raphael Badin
Roll back 12977.
339
            html_content = view()
12792.7.9 by Raphael Badin
Merge devel.
340
13168.8.7 by Gavin Panella
Remove DistroSeries.is_initialising.
341
        self.assertTrue(derived_series.isInitializing())
12981.1.2 by Raphael Badin
Roll back 12977.
342
        self.assertThat(html_content, portlet_display)
12792.7.9 by Raphael Badin
Merge devel.
343
13375.4.11 by Gavin Panella
Show that initialization has failed on DistroSeries:+index.
344
    def test_differences_portlet_initialization_failed(self):
345
        # The difference portlet displays a failure message if initialization
346
        # for the series has failed.
347
        set_derived_series_ui_feature_flag(self)
348
        derived_series = self.factory.makeDistroSeries()
349
        parent_series = self.factory.makeDistroSeries()
350
        self.simple_user = self.factory.makePerson()
351
        job_source = getUtility(IInitializeDistroSeriesJobSource)
352
        job = job_source.create(derived_series, [parent_series.id])
353
        job.start()
354
        job.fail()
355
        portlet_display = soupmatchers.HTMLContains(
356
            soupmatchers.Tag(
357
                'Derived series', 'h2',
358
                text='Series initialization has failed'),
359
            )
360
        with person_logged_in(self.simple_user):
361
            view = create_initialized_view(
13375.4.12 by Gavin Panella
The last revision's commit message was a lie. *This* revision ensures that the failure message is shown on DistroSeries:+index; the previous revision only checked DistroSeries:+portlet-derivation.
362
                derived_series, '+index', principal=self.simple_user)
13375.4.11 by Gavin Panella
Show that initialization has failed on DistroSeries:+index.
363
            html_content = view()
364
        self.assertThat(html_content, portlet_display)
365
13199.3.1 by Raphael Badin
Add +initseries link.
366
    def assertInitSeriesLinkPresent(self, series, person):
367
        self._assertInitSeriesLink(series, person, True)
368
369
    def assertInitSeriesLinkNotPresent(self, series, person):
370
        self._assertInitSeriesLink(series, person, False)
371
372
    def _assertInitSeriesLink(self, series, person, present=True):
373
        # Helper method to check the presence/absence of the link to
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
374
        # +initseries.
13199.3.1 by Raphael Badin
Add +initseries link.
375
        if person == 'admin':
376
            person = getUtility(ILaunchpadCelebrities).admin.teamowner
377
378
        init_link_matcher = soupmatchers.HTMLContains(
379
            soupmatchers.Tag(
380
                'Initialize series', 'a',
381
                text='Initialize series',
13199.3.5 by Raphael Badin
Use new isInitializing and isInitialized method from IDS.
382
                attrs={'href': '%s/+initseries' % canonical_url(series)}))
13199.3.1 by Raphael Badin
Add +initseries link.
383
384
        with person_logged_in(person):
385
            view = create_initialized_view(
386
                series,
387
                '+index',
388
                principal=person)
389
            html_content = view()
390
391
        if present:
392
            self.assertThat(html_content, init_link_matcher)
393
        else:
394
            self.assertThat(html_content, Not(init_link_matcher))
395
396
    def test_differences_init_link_no_feature(self):
397
        # The link to +initseries is not displayed if the feature flag
398
        # is not enabled.
399
        series = self.factory.makeDistroSeries()
400
401
        self.assertInitSeriesLinkNotPresent(series, 'admin')
402
403
    def test_differences_init_link_admin(self):
404
        # The link to +initseries is displayed to admin users if the
405
        # feature flag is enabled.
406
        set_derived_series_ui_feature_flag(self)
407
        series = self.factory.makeDistroSeries()
408
409
        self.assertInitSeriesLinkPresent(series, 'admin')
410
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
411
    def test_differences_init_link_series_driver(self):
412
        # The link to +initseries is displayed to the distroseries's
413
        # drivers.
414
        set_derived_series_ui_feature_flag(self)
415
        distroseries = self.factory.makeDistroSeries()
416
        driver = self.factory.makePerson()
417
        with celebrity_logged_in('admin'):
418
            distroseries.driver = driver
419
420
        self.assertInitSeriesLinkPresent(distroseries, driver)
421
13199.3.1 by Raphael Badin
Add +initseries link.
422
    def test_differences_init_link_not_admin(self):
423
        # The link to +initseries is not displayed to not admin users if the
424
        # feature flag is enabled.
425
        set_derived_series_ui_feature_flag(self)
426
        series = self.factory.makeDistroSeries()
427
        person = self.factory.makePerson()
428
429
        self.assertInitSeriesLinkNotPresent(series, person)
430
13199.3.2 by Raphael Badin
Initialise -> initialize.
431
    def test_differences_init_link_initialized(self):
13199.3.1 by Raphael Badin
Add +initseries link.
432
        # The link to +initseries is not displayed if the series is
13199.3.5 by Raphael Badin
Use new isInitializing and isInitialized method from IDS.
433
        # already initialized (i.e. has any published package).
13199.3.1 by Raphael Badin
Add +initseries link.
434
        set_derived_series_ui_feature_flag(self)
435
        series = self.factory.makeDistroSeries()
13199.3.5 by Raphael Badin
Use new isInitializing and isInitialized method from IDS.
436
        self.factory.makeSourcePackagePublishingHistory(
437
            archive=series.main_archive,
438
            distroseries=series)
13199.3.1 by Raphael Badin
Add +initseries link.
439
440
        self.assertInitSeriesLinkNotPresent(series, 'admin')
441
442
    def test_differences_init_link_series_initializing(self):
443
        # The link to +initseries is not displayed if the series is
13199.3.2 by Raphael Badin
Initialise -> initialize.
444
        # initializing.
13199.3.1 by Raphael Badin
Add +initseries link.
445
        set_derived_series_ui_feature_flag(self)
446
        series = self.factory.makeDistroSeries()
447
        parent_series = self.factory.makeDistroSeries()
13194.2.8 by Gavin Panella
One more change of spelling.
448
        job_source = getUtility(IInitializeDistroSeriesJobSource)
13199.3.1 by Raphael Badin
Add +initseries link.
449
        job_source.create(series, [parent_series.id])
450
451
        self.assertInitSeriesLinkNotPresent(series, 'admin')
452
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
453
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
454
class TestDistroSeriesDerivationPortlet(TestCaseWithFactory):
455
456
    layer = LaunchpadFunctionalLayer
457
458
    @property
459
    def job_source(self):
460
        return getUtility(IInitializeDistroSeriesJobSource)
461
462
    def test_initialization_failed_can_retry(self):
463
        # When initialization has failed and the user has the ability to retry
464
        # it prompts the user to try again.
465
        set_derived_series_ui_feature_flag(self)
466
        series = self.factory.makeDistroSeries()
467
        parent = self.factory.makeDistroSeries()
468
        job = self.job_source.create(series, [parent.id])
469
        job.start()
470
        job.fail()
471
        with person_logged_in(series.owner):
472
            view = create_initialized_view(series, '+portlet-derivation')
473
            html_content = view()
474
        self.assertThat(
475
            extract_text(html_content), DocTestMatches(
476
                "Series initialization has failed\n"
477
                "You can attempt initialization again."))
478
479
    def test_initialization_failed_cannot_retry(self):
480
        # When initialization has failed and the user does not have the
481
        # ability to retry it suggests contacting someone who can.
482
        set_derived_series_ui_feature_flag(self)
483
        series = self.factory.makeDistroSeries()
484
        parent = self.factory.makeDistroSeries()
485
        job = self.job_source.create(series, [parent.id])
486
        job.start()
487
        job.fail()
13375.4.17 by Gavin Panella
Use real names in the tests for DistroSeries:+portlet-derivation.
488
        with person_logged_in(series.distribution.owner):
489
            series.distribution.owner.displayname = u"Bob Individual"
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
490
        with anonymous_logged_in():
491
            view = create_initialized_view(series, '+portlet-derivation')
492
            html_content = view()
493
        self.assertThat(
494
            extract_text(html_content), DocTestMatches(
495
                "Series initialization has failed\n"
496
                "You cannot attempt initialization again, "
13375.4.17 by Gavin Panella
Use real names in the tests for DistroSeries:+portlet-derivation.
497
                "but Bob Individual may be able to help."))
13375.4.16 by Gavin Panella
Merge two tests because it doesn't add clarity to have them separate, and there's an overhead to having them separate.
498
        # When the owner is a team the message differs slightly from when the
499
        # owner is an individual.
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
500
        with person_logged_in(series.distribution.owner):
13375.4.17 by Gavin Panella
Use real names in the tests for DistroSeries:+portlet-derivation.
501
            series.distribution.owner = self.factory.makeTeam(
502
                displayname=u"Team Teamy Team Team")
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
503
        with anonymous_logged_in():
504
            view = create_initialized_view(series, '+portlet-derivation')
505
            html_content = view()
506
        self.assertThat(
507
            extract_text(html_content), DocTestMatches(
508
                "Series initialization has failed\n"
13375.4.17 by Gavin Panella
Use real names in the tests for DistroSeries:+portlet-derivation.
509
                "You cannot attempt initialization again, but a "
510
                "member of Team Teamy Team Team may be able to help."))
13375.4.13 by Gavin Panella
Show an explanatory message when initialization has failed.
511
512
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
513
class TestMilestoneBatchNavigatorAttribute(TestCaseWithFactory):
514
    """Test the series.milestone_batch_navigator attribute."""
515
516
    layer = LaunchpadZopelessLayer
517
518
    def test_distroseries_milestone_batch_navigator(self):
519
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
520
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
521
        for name in ('a', 'b', 'c', 'd'):
522
            distroseries.newMilestone(name)
523
        view = create_initialized_view(distroseries, name='+index')
524
        self._check_milestone_batch_navigator(view)
525
526
    def test_productseries_milestone_batch_navigator(self):
527
        product = self.factory.makeProduct()
528
        for name in ('a', 'b', 'c', 'd'):
529
            product.development_focus.newMilestone(name)
530
531
        view = create_initialized_view(
532
            product.development_focus, name='+index')
533
        self._check_milestone_batch_navigator(view)
534
535
    def _check_milestone_batch_navigator(self, view):
536
        config.push('default-batch-size', """
537
        [launchpad]
538
        default_batch_size: 2
539
        """)
540
        self.assert_(
541
            isinstance(view.milestone_batch_navigator, BatchNavigator),
542
            'milestone_batch_navigator is not a BatchNavigator object: %r'
543
            % view.milestone_batch_navigator)
544
        self.assertEqual(4, view.milestone_batch_navigator.batch.total())
545
        expected = [
546
            'd',
547
            'c',
548
            ]
549
        milestone_names = [
550
            item.name
551
            for item in view.milestone_batch_navigator.currentBatch()]
552
        self.assertEqual(expected, milestone_names)
553
        config.pop('default-batch-size')
554
555
12736.4.6 by Gavin Panella
Demonstrate how the DistroSeries created in +addseries is initialized.
556
class TestDistroSeriesAddView(TestCaseWithFactory):
557
558
    layer = DatabaseFunctionalLayer
559
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
560
    def setUp(self):
561
        super(TestDistroSeriesAddView, self).setUp()
562
        self.user = self.factory.makePerson()
563
        self.distribution = self.factory.makeDistribution(owner=self.user)
564
565
    def createNewDistroseries(self):
12736.4.6 by Gavin Panella
Demonstrate how the DistroSeries created in +addseries is initialized.
566
        form = {
567
            "field.name": u"polished",
568
            "field.version": u"12.04",
569
            "field.displayname": u"Polished Polecat",
570
            "field.summary": u"Even The Register likes it.",
571
            "field.actions.create": u"Add Series",
572
            }
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
573
        with person_logged_in(self.user):
574
            create_initialized_view(self.distribution, "+addseries",
575
                                    form=form)
576
        distroseries = self.distribution.getSeries(u"polished")
577
        return distroseries
578
579
    def assertCreated(self, distroseries):
12736.4.6 by Gavin Panella
Demonstrate how the DistroSeries created in +addseries is initialized.
580
        self.assertEqual(u"polished", distroseries.name)
581
        self.assertEqual(u"12.04", distroseries.version)
582
        self.assertEqual(u"Polished Polecat", distroseries.displayname)
583
        self.assertEqual(u"Polished Polecat", distroseries.title)
584
        self.assertEqual(u"Even The Register likes it.", distroseries.summary)
585
        self.assertEqual(u"", distroseries.description)
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
586
        self.assertEqual(self.user, distroseries.owner)
587
588
    def test_plain_submit(self):
589
        # When creating a new DistroSeries via DistroSeriesAddView, the title
590
        # is set to the same as the displayname (title is, in any case,
591
        # deprecated), the description is left empty, and previous_series is
592
        # None (DistroSeriesInitializeView takes care of setting that).
593
        distroseries = self.createNewDistroseries()
594
        self.assertCreated(distroseries)
13045.14.3 by Gavin Panella
Change many more sites from using parent_series to previous_series.
595
        self.assertIs(None, distroseries.previous_series)
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
596
597
    def test_submit_sets_previous_series(self):
598
        # Creating a new series when one already exists should set the
599
        # previous_series.
13444.6.28 by Gavin Panella
Fix lint.
600
        old_series = self.factory.makeDistroSeries(
601
            self.distribution, version='11.10')
602
        # A yet older series.
603
        self.factory.makeDistroSeries(
604
            self.distribution, version='11.04')
13372.1.4 by Julian Edwards
fix lint
605
        old_time = utc_now() - timedelta(days=5)
13372.1.3 by Julian Edwards
createAndAdd now ensures that previous_series is automatically set when
606
        removeSecurityProxy(old_series).datereleased = old_time
607
        distroseries = self.createNewDistroseries()
608
        self.assertEqual(old_series, distroseries.previous_series)
12736.4.6 by Gavin Panella
Demonstrate how the DistroSeries created in +addseries is initialized.
609
610
12494.1.17 by Gavin Panella
Resume work on +initseries.
611
class TestDistroSeriesInitializeView(TestCaseWithFactory):
612
613
    layer = DatabaseFunctionalLayer
614
615
    def test_init(self):
616
        # There exists a +initseries view for distroseries.
617
        distroseries = self.factory.makeDistroSeries()
618
        view = create_initialized_view(distroseries, "+initseries")
619
        self.assertTrue(view)
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
620
12494.1.97 by Gavin Panella
Rename is_feature_enabled to is_derived_series_feature_enabled.
621
    def test_is_derived_series_feature_enabled(self):
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
622
        # The feature is disabled by default, but can be enabled by setting
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
623
        # the soyuz.derived_series_ui.enabled flag.
12494.1.93 by Gavin Panella
Add is_feature_enabled to DistroSeriesInitializeView.
624
        distroseries = self.factory.makeDistroSeries()
625
        view = create_initialized_view(distroseries, "+initseries")
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
626
        with FeatureFixture({}):
12494.1.97 by Gavin Panella
Rename is_feature_enabled to is_derived_series_feature_enabled.
627
            self.assertFalse(view.is_derived_series_feature_enabled)
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
628
        flags = {u"soyuz.derived_series_ui.enabled": u"true"}
629
        with FeatureFixture(flags):
12494.1.97 by Gavin Panella
Rename is_feature_enabled to is_derived_series_feature_enabled.
630
            self.assertTrue(view.is_derived_series_feature_enabled)
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
631
12494.1.97 by Gavin Panella
Rename is_feature_enabled to is_derived_series_feature_enabled.
632
    def test_form_hidden_when_derived_series_feature_disabled(self):
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
633
        # The form is hidden when the feature flag is not set.
634
        distroseries = self.factory.makeDistroSeries()
635
        view = create_initialized_view(distroseries, "+initseries")
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
636
        with FeatureFixture({}):
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
637
            root = html.fromstring(view())
638
            self.assertEqual(
12494.1.106 by Gavin Panella
Some renames; simplify the success message.
639
                [], root.cssselect("#initseries-form-container"))
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
640
            # Instead an explanatory message is shown.
641
            [message] = root.cssselect("p.error.message")
642
            self.assertIn(
643
                u"The Derivative Distributions feature is under development",
644
                message.text)
645
12494.1.97 by Gavin Panella
Rename is_feature_enabled to is_derived_series_feature_enabled.
646
    def test_form_shown_when_derived_series_feature_enabled(self):
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
647
        # The form is shown when the feature flag is set.
648
        distroseries = self.factory.makeDistroSeries()
649
        view = create_initialized_view(distroseries, "+initseries")
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
650
        flags = {u"soyuz.derived_series_ui.enabled": u"true"}
651
        with FeatureFixture(flags):
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
652
            root = html.fromstring(view())
653
            self.assertNotEqual(
12494.1.106 by Gavin Panella
Some renames; simplify the success message.
654
                [], root.cssselect("#initseries-form-container"))
12494.1.94 by Gavin Panella
Don't include the view's form in +initseries if the feature flag is not set.
655
            # A different explanatory message is shown for clients that don't
656
            # process Javascript.
657
            [message] = root.cssselect("p.error.message")
658
            self.assertIn(
659
                u"Javascript is required to use this page",
660
                message.text)
661
            self.assertIn(
662
                u"javascript-disabled",
663
                message.get("class").split())
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
664
13168.14.5 by Raphael Badin
Refactor test_not_is_first_derivation tests.
665
    def test_seriesToVocab(self):
666
        distroseries = self.factory.makeDistroSeries()
667
        formatted_dict = seriesToVocab(distroseries)
668
669
        self.assertEquals(
670
            ['api_uri', 'title', 'value'],
671
            sorted(formatted_dict.keys()))
672
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
673
    def test_is_first_derivation(self):
674
        # If the distro has no initialized series, this initialization
675
        # is a 'first_derivation'.
7675.1189.6 by Raphael Badin
Add DistroSeriesInitializeView.rebuilding_allowed.
676
        distroseries = self.factory.makeDistroSeries()
677
        self.factory.makeDistroSeries(
678
            distribution=distroseries.distribution)
679
        view = create_initialized_view(distroseries, "+initseries")
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
680
        cache = IJSONRequestCache(view.request).objects
681
682
        self.assertTrue(cache['is_first_derivation'])
683
684
    def test_not_is_first_derivation(self):
685
        # If the distro has an initialized series, this initialization
686
        # is not a 'first_derivation'. The previous_series and the
687
        # previous_series' parents are in LP.cache to be used by
688
        # Javascript on the +initseries page.
689
        previous_series = self.factory.makeDistroSeries()
690
        previous_parent1 = self.factory.makeDistroSeriesParent(
691
            derived_series=previous_series).parent_series
692
        previous_parent2 = self.factory.makeDistroSeriesParent(
693
            derived_series=previous_series).parent_series
694
        distroseries = self.factory.makeDistroSeries(
695
            previous_series=previous_series)
7675.1189.6 by Raphael Badin
Add DistroSeriesInitializeView.rebuilding_allowed.
696
        another_distroseries = self.factory.makeDistroSeries(
697
            distribution=distroseries.distribution)
698
        self.factory.makeSourcePackagePublishingHistory(
699
            distroseries=another_distroseries)
700
        view = create_initialized_view(distroseries, "+initseries")
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
701
        cache = IJSONRequestCache(view.request).objects
702
703
        self.assertFalse(cache['is_first_derivation'])
13168.14.5 by Raphael Badin
Refactor test_not_is_first_derivation tests.
704
        self.assertContentEqual(
705
            seriesToVocab(previous_series),
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
706
            cache['previous_series'])
707
        self.assertEqual(
708
            2,
709
            len(cache['previous_parents']))
13168.14.5 by Raphael Badin
Refactor test_not_is_first_derivation tests.
710
        self.assertContentEqual(
711
            seriesToVocab(previous_parent1),
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
712
            cache['previous_parents'][0])
13168.14.5 by Raphael Badin
Refactor test_not_is_first_derivation tests.
713
        self.assertContentEqual(
714
            seriesToVocab(previous_parent2),
13168.14.1 by Raphael Badin
Fix the UI (initseries) to support the non first initialization use-case.
715
            cache['previous_parents'][1])
7675.1189.6 by Raphael Badin
Add DistroSeriesInitializeView.rebuilding_allowed.
716
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
717
    def test_form_hidden_when_distroseries_is_initialized(self):
718
        # The form is hidden when the feature flag is set but the series has
13168.8.5 by Gavin Panella
Change the message shown when the distroseries has already been initialized.
719
        # already been initialized.
13168.14.6 by Raphael Badin
Fix test.
720
        distroseries = self.factory.makeDistroSeries(
721
            previous_series=self.factory.makeDistroSeries())
13168.8.8 by Gavin Panella
Use the new DistroSeries methods on +initseries.
722
        self.factory.makeSourcePackagePublishingHistory(
723
            distroseries=distroseries, archive=distroseries.main_archive)
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
724
        view = create_initialized_view(distroseries, "+initseries")
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
725
        flags = {u"soyuz.derived_series_ui.enabled": u"true"}
726
        with FeatureFixture(flags):
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
727
            root = html.fromstring(view())
728
            self.assertEqual(
729
                [], root.cssselect("#initseries-form-container"))
730
            # Instead an explanatory message is shown.
731
            [message] = root.cssselect("p.error.message")
13168.8.5 by Gavin Panella
Change the message shown when the distroseries has already been initialized.
732
            self.assertThat(
13168.8.11 by Gavin Panella
No need to strip the message when using EqualsIgnoringWhitespace.
733
                message.text, EqualsIgnoringWhitespace(
13168.8.5 by Gavin Panella
Change the message shown when the distroseries has already been initialized.
734
                    u"This series already contains source packages "
735
                    u"and cannot be initialized again."))
13168.8.1 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries has already been derived.
736
13168.8.2 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries is in the process of being derived.
737
    def test_form_hidden_when_distroseries_is_being_initialized(self):
738
        # The form is hidden when the feature flag is set but the series has
739
        # already been derived.
740
        distroseries = self.factory.makeDistroSeries()
13194.2.4 by Gavin Panella
Merge devel, resolving several minor conflicts.
741
        getUtility(IInitializeDistroSeriesJobSource).create(
13168.8.2 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries is in the process of being derived.
742
            distroseries, [self.factory.makeDistroSeries().id])
743
        view = create_initialized_view(distroseries, "+initseries")
13168.8.3 by Gavin Panella
Use FeatureFixture instead of feature_flags and set_feature_flag.
744
        flags = {u"soyuz.derived_series_ui.enabled": u"true"}
745
        with FeatureFixture(flags):
13168.8.2 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries is in the process of being derived.
746
            root = html.fromstring(view())
747
            self.assertEqual(
748
                [], root.cssselect("#initseries-form-container"))
749
            # Instead an explanatory message is shown.
750
            [message] = root.cssselect("p.error.message")
13168.8.5 by Gavin Panella
Change the message shown when the distroseries has already been initialized.
751
            self.assertThat(
13168.8.11 by Gavin Panella
No need to strip the message when using EqualsIgnoringWhitespace.
752
                message.text, EqualsIgnoringWhitespace(
13168.8.5 by Gavin Panella
Change the message shown when the distroseries has already been initialized.
753
                    u"This series is already being initialized."))
13168.8.2 by Gavin Panella
Show a message on +initseries, and prevent further derivation, when the distroseries is in the process of being derived.
754
13371.2.1 by Raphael Badin
Display error message if previous_series is None when initializing a series.
755
    def test_form_hidden_when_previous_series_none(self):
756
        # If the distribution has an initialized series and the
757
        # distroseries' previous_series is None: the form is hidden and
758
        # the page contains an error message.
759
        distroseries = self.factory.makeDistroSeries(
760
            previous_series=None)
761
        another_distroseries = self.factory.makeDistroSeries(
762
            distribution=distroseries.distribution)
763
        self.factory.makeSourcePackagePublishingHistory(
764
            distroseries=another_distroseries)
765
        view = create_initialized_view(distroseries, "+initseries")
766
        flags = {u"soyuz.derived_series_ui.enabled": u"true"}
767
        with FeatureFixture(flags):
768
            root = html.fromstring(view())
769
            self.assertEqual(
770
                [], root.cssselect("#initseries-form-container"))
771
            # Instead an explanatory message is shown.
772
            [message] = root.cssselect("p.error.message")
773
            self.assertThat(
774
                message.text, EqualsIgnoringWhitespace(
13371.2.3 by Raphael Badin
Use 'initialize'.
775
                    u'Unable to initialize series: the distribution '
776
                    u'already has initialized series and this distroseries '
13371.2.1 by Raphael Badin
Display error message if previous_series is None when initializing a series.
777
                    u'has no previous series.'))
778
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
779
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
780
class TestDistroSeriesInitializeViewAccess(TestCaseWithFactory):
781
    """Test access to IDS.+initseries."""
782
783
    layer = LaunchpadFunctionalLayer
784
785
    def setUp(self):
786
        super(TestDistroSeriesInitializeViewAccess,
787
              self).setUp('foo.bar@canonical.com')
788
        set_derived_series_ui_feature_flag(self)
789
13261.4.2 by Raphael Badin
Add a test for anonymous access.
790
    def test_initseries_access_anon(self):
791
        # Anonymous users cannot access +initseries.
792
        distroseries = self.factory.makeDistroSeries()
793
        view = create_initialized_view(distroseries, "+initseries")
794
        login(ANONYMOUS)
795
796
        self.assertEqual(
797
            False,
798
            check_permission('launchpad.Edit', view))
799
13261.4.1 by Raphael Badin
Change permission to access IDS:+initseries from launchpad.Admin to launchpad.Edit.
800
    def test_initseries_access_simpleuser(self):
801
        # Unprivileged users cannot access +initseries.
802
        distroseries = self.factory.makeDistroSeries()
803
        view = create_initialized_view(distroseries, "+initseries")
804
        login_person(self.factory.makePerson())
805
806
        self.assertEqual(
807
            False,
808
            check_permission('launchpad.Edit', view))
809
810
    def test_initseries_access_admin(self):
811
        # Users with lp.Admin can access +initseries.
812
        distroseries = self.factory.makeDistroSeries()
813
        view = create_initialized_view(distroseries, "+initseries")
814
        login_celebrity('admin')
815
816
        self.assertEqual(
817
            True,
818
            check_permission('launchpad.Edit', view))
819
820
    def test_initseries_access_driver(self):
821
        # Distroseries drivers can access +initseries.
822
        distroseries = self.factory.makeDistroSeries()
823
        view = create_initialized_view(distroseries, "+initseries")
824
        driver = self.factory.makePerson()
825
        with celebrity_logged_in('admin'):
826
            distroseries.driver = driver
827
        login_person(driver)
828
829
        self.assertEqual(
830
            True,
831
            check_permission('launchpad.Edit', view))
832
833
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
834
class DistroSeriesDifferenceMixin:
835
    """A helper class for testing differences pages"""
836
12981.1.7 by Raphael Badin
Merge devel.
837
    def _test_packagesets(self, html_content, packageset_text,
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
838
                          packageset_class, msg_text):
839
        parent_packagesets = soupmatchers.HTMLContains(
840
            soupmatchers.Tag(
841
                msg_text, 'td',
842
                attrs={'class': packageset_class},
843
                text=packageset_text))
844
12981.1.7 by Raphael Badin
Merge devel.
845
        self.assertThat(html_content, parent_packagesets)
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
846
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
847
    def _createChildAndParent(self):
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
848
        derived_series = self.factory.makeDistroSeries(name='derilucid')
849
        parent_series = self.factory.makeDistroSeries(name='lucid')
850
        self.factory.makeDistroSeriesParent(
851
            derived_series=derived_series, parent_series=parent_series)
852
        return (derived_series, parent_series)
853
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
854
    def _createChildAndParents(self, other_parent_series=None):
855
        derived_series, parent_series = self._createChildAndParent()
856
        self.factory.makeDistroSeriesParent(
857
            derived_series=derived_series, parent_series=other_parent_series)
858
        return (derived_series, parent_series)
859
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
860
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
861
class TestDistroSeriesLocalDiffPerformance(TestCaseWithFactory,
862
                                           DistroSeriesDifferenceMixin):
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
863
    """Test the distroseries +localpackagediffs page's performance."""
864
865
    layer = DatabaseFunctionalLayer
866
867
    def setUp(self):
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
868
        super(TestDistroSeriesLocalDiffPerformance,
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
869
             self).setUp('foo.bar@canonical.com')
870
        set_derived_series_ui_feature_flag(self)
871
        self.simple_user = self.factory.makePerson()
872
873
    def _assertQueryCount(self, derived_series):
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
874
        # With no DistroSeriesDifferences the query count should be low and
875
        # fairly static. However, with some DistroSeriesDifferences the query
876
        # count will be higher, but it should remain the same no matter how
877
        # many differences there are.
12981.1.2 by Raphael Badin
Roll back 12977.
878
        ArchivePermission(
879
            archive=derived_series.main_archive, person=self.simple_user,
880
            component=getUtility(IComponentSet)["main"],
881
            permission=ArchivePermissionType.QUEUE_ADMIN)
882
        login_person(self.simple_user)
12742.3.12 by Gavin Panella
Preload stuff needed for expanding source_pub/sourcepackagerelease/uploader.
883
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
884
        def add_differences(num):
885
            for index in xrange(num):
12742.3.19 by Gavin Panella
Never use the same version twice, just in case it matters.
886
                version = self.factory.getUniqueInteger()
887
                versions = {
888
                    'base': u'1.%d' % version,
889
                    'derived': u'1.%dderived1' % version,
890
                    'parent': u'1.%d-1' % version,
891
                    }
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
892
                dsd = self.factory.makeDistroSeriesDifference(
893
                    derived_series=derived_series,
894
                    versions=versions)
895
896
                # Push a base_version in... not sure how better to do it.
897
                removeSecurityProxy(dsd).base_version = versions["base"]
898
899
                # Add a couple of comments.
900
                self.factory.makeDistroSeriesDifferenceComment(dsd)
901
                self.factory.makeDistroSeriesDifferenceComment(dsd)
902
903
                # Update the spr, some with recipes, some with signing keys.
904
                # SPR.uploader references both, and the uploader is referenced
905
                # in the page.
906
                spr = dsd.source_pub.sourcepackagerelease
907
                if index % 2 == 0:
908
                    removeSecurityProxy(spr).source_package_recipe_build = (
909
                        self.factory.makeSourcePackageRecipeBuild(
910
                            sourcename=spr.sourcepackagename.name,
911
                            distroseries=derived_series))
912
                else:
913
                    removeSecurityProxy(spr).dscsigningkey = (
914
                        self.factory.makeGPGKey(owner=spr.creator))
915
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
916
        def flush_and_render():
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
917
            flush_database_caches()
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
918
            # Pull in the calling user's location so that it isn't recorded in
919
            # the query count; it causes the total to be fragile for no
920
            # readily apparent reason.
921
            self.simple_user.location
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
922
            with StormStatementRecorder() as recorder:
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
923
                view = create_initialized_view(
12742.3.21 by Gavin Panella
Capture queries for initializing and rendering the view, and wrap queries in the error details.
924
                    derived_series, '+localpackagediffs',
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
925
                    principal=self.simple_user)
926
                view()
927
            return recorder, view.cached_differences.batch.trueSize
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
928
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
929
        def statement_differ(rec1, rec2):
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
930
            wrapper = TextWrapper(break_long_words=False)
931
12742.3.21 by Gavin Panella
Capture queries for initializing and rendering the view, and wrap queries in the error details.
932
            def prepare_statements(rec):
933
                for statement in rec.statements:
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
934
                    for line in wrapper.wrap(statement):
12742.3.21 by Gavin Panella
Capture queries for initializing and rendering the view, and wrap queries in the error details.
935
                        yield line
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
936
                    yield "-" * wrapper.width
937
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
938
            def statement_diff():
12742.3.21 by Gavin Panella
Capture queries for initializing and rendering the view, and wrap queries in the error details.
939
                diff = difflib.ndiff(
940
                    list(prepare_statements(rec1)),
941
                    list(prepare_statements(rec2)))
942
                for line in diff:
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
943
                    yield "%s\n" % line
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
944
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
945
            return statement_diff
946
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
947
        # Render without differences and check the query count isn't silly.
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
948
        recorder1, batch_size = flush_and_render()
12742.3.22 by Gavin Panella
Wrap the queries reported when the test breaks, and load SourcePackageNames.
949
        self.assertThat(recorder1, HasQueryCount(LessThan(30)))
12742.3.38 by Gavin Panella
Add statement counts for 0, 2, and 4 difference rows.
950
        self.addDetail(
951
            "statement-count-0-differences",
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
952
            text_content(u"%d" % recorder1.count))
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
953
        # Add some differences and render.
12742.3.21 by Gavin Panella
Capture queries for initializing and rendering the view, and wrap queries in the error details.
954
        add_differences(2)
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
955
        recorder2, batch_size = flush_and_render()
12742.3.38 by Gavin Panella
Add statement counts for 0, 2, and 4 difference rows.
956
        self.addDetail(
957
            "statement-count-2-differences",
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
958
            text_content(u"%d" % recorder2.count))
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
959
        # Add more differences and render again.
960
        add_differences(2)
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
961
        recorder3, batch_size = flush_and_render()
12742.3.38 by Gavin Panella
Add statement counts for 0, 2, and 4 difference rows.
962
        self.addDetail(
963
            "statement-count-4-differences",
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
964
            text_content(u"%d" % recorder3.count))
12742.3.16 by Gavin Panella
Add comments to the differences, and make the test less fragile.
965
        # The last render should not need more queries than the previous.
12742.3.18 by Gavin Panella
Show a diff of SQL statements between the second and third render.
966
        self.addDetail(
967
            "statement-diff", Content(
968
                UTF8_TEXT, statement_differ(recorder2, recorder3)))
12742.3.43 by Gavin Panella
Calculate the average queries-per-row and record it as a test detail.
969
        # Details about the number of statements per row.
970
        statement_count_per_row = (
971
            (recorder3.count - recorder1.count) / float(batch_size))
972
        self.addDetail(
973
            "statement-count-per-row-average",
974
            text_content(u"%.2f" % statement_count_per_row))
7675.1137.6 by Raphael Badin
Cache latest comments' message chunks.
975
        # Query count is ~O(1) (i.e. not dependent of the number of
976
        # differences displayed).
12742.3.23 by Gavin Panella
Add XXX about statement counts.
977
        self.assertThat(
7675.1137.8 by Raphael Badin
Refactor grouping method to use defaultdict which is more compact and mmore efficient.
978
            recorder3, HasQueryCount(Equals(recorder2.count)))
12742.3.10 by Gavin Panella
Keep track of all queries.
979
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
980
    def test_queries_single_parent(self):
981
        dsp = self.factory.makeDistroSeriesParent()
982
        derived_series = dsp.derived_series
983
        self._assertQueryCount(derived_series)
984
985
    def test_queries_multiple_parents(self):
986
        dsp = self.factory.makeDistroSeriesParent()
987
        derived_series = dsp.derived_series
988
        self.factory.makeDistroSeriesParent(
989
            derived_series=derived_series)
990
        self._assertQueryCount(derived_series)
991
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
992
13444.6.32 by Gavin Panella
More truthiness in test names.
993
class TestDistroSeriesLocalDifferences(TestCaseWithFactory,
994
                                       DistroSeriesDifferenceMixin):
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
995
    """Test the distroseries +localpackagediffs view."""
996
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
997
    layer = LaunchpadFunctionalLayer
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
998
12959.2.21 by Gavin Panella
Test to demonstrate that requestUpgrade() is efficient with the database.
999
    def makePackageUpgrade(self, derived_series=None):
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1000
        """Create a `DistroSeriesDifference` for a package upgrade."""
1001
        base_version = '1.%d' % self.factory.getUniqueInteger()
1002
        versions = {
1003
            'base': base_version,
1004
            'parent': base_version + '-' + self.factory.getUniqueString(),
1005
            'derived': base_version,
1006
        }
1007
        return self.factory.makeDistroSeriesDifference(
12959.2.21 by Gavin Panella
Test to demonstrate that requestUpgrade() is efficient with the database.
1008
            derived_series=derived_series, versions=versions,
1009
            set_base_version=True)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1010
1011
    def makeView(self, distroseries=None):
1012
        """Create a +localpackagediffs view for `distroseries`."""
1013
        if distroseries is None:
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1014
            distroseries = (
1015
                self.factory.makeDistroSeriesParent().derived_series)
12959.2.28 by Gavin Panella
Fix last few test failures, phew.
1016
        # current_request=True causes the current interaction to end so we
1017
        # must explicitly ask that the current principal be used for the
1018
        # request.
1019
        return create_initialized_view(
1020
            distroseries, '+localpackagediffs',
1021
            principal=get_current_principal(),
1022
            current_request=True)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1023
13444.6.42 by Gavin Panella
Move all the tests in TestDistroSeriesLocalDifferences_WRONG_LAYER into TestDistroSeriesLocalDifferences.
1024
    def test_filter_form_if_differences(self):
1025
        # Test that the page includes the filter form if differences
1026
        # are present
1027
        simple_user = self.factory.makePerson()
1028
        login_person(simple_user)
1029
        derived_series, parent_series = self._createChildAndParent()
1030
        self.factory.makeDistroSeriesDifference(
1031
            derived_series=derived_series)
1032
1033
        set_derived_series_ui_feature_flag(self)
1034
        view = create_initialized_view(
1035
            derived_series, '+localpackagediffs', principal=simple_user)
1036
1037
        self.assertIsNot(
1038
            None,
1039
            find_tag_by_id(view(), 'distroseries-localdiff-search-filter'),
1040
            "Form filter should be shown when there are differences.")
1041
1042
    def test_filter_noform_if_nodifferences(self):
1043
        # Test that the page doesn't includes the filter form if no
1044
        # differences are present
1045
        simple_user = self.factory.makePerson()
1046
        login_person(simple_user)
1047
        derived_series, parent_series = self._createChildAndParent()
1048
1049
        set_derived_series_ui_feature_flag(self)
1050
        view = create_initialized_view(
1051
            derived_series, '+localpackagediffs', principal=simple_user)
1052
1053
        self.assertIs(
1054
            None,
1055
            find_tag_by_id(view(), 'distroseries-localdiff-search-filter'),
1056
            "Form filter should not be shown when there are no differences.")
1057
1058
    def test_parent_packagesets_localpackagediffs(self):
1059
        # +localpackagediffs displays the packagesets.
1060
        ds_diff = self.factory.makeDistroSeriesDifference()
1061
        with celebrity_logged_in('admin'):
1062
            ps = self.factory.makePackageset(
1063
                packages=[ds_diff.source_package_name],
1064
                distroseries=ds_diff.derived_series)
1065
1066
        set_derived_series_ui_feature_flag(self)
1067
        simple_user = self.factory.makePerson()
1068
        with person_logged_in(simple_user):
1069
            view = create_initialized_view(
1070
                ds_diff.derived_series,
1071
                '+localpackagediffs',
1072
                principal=simple_user)
1073
            html_content = view()
1074
1075
        packageset_text = re.compile('\s*' + ps.name)
1076
        self._test_packagesets(
1077
            html_content, packageset_text, 'packagesets',
1078
            'Packagesets')
1079
1080
    def test_parent_packagesets_localpackagediffs_sorts(self):
1081
        # Multiple packagesets are sorted in a comma separated list.
1082
        ds_diff = self.factory.makeDistroSeriesDifference()
1083
        unsorted_names = [u"zzz", u"aaa"]
1084
        with celebrity_logged_in('admin'):
1085
            for name in unsorted_names:
1086
                self.factory.makePackageset(
1087
                    name=name,
1088
                    packages=[ds_diff.source_package_name],
1089
                    distroseries=ds_diff.derived_series)
1090
1091
        set_derived_series_ui_feature_flag(self)
1092
        simple_user = self.factory.makePerson()
1093
        with person_logged_in(simple_user):
1094
            view = create_initialized_view(
1095
                ds_diff.derived_series,
1096
                '+localpackagediffs',
1097
                principal=simple_user)
1098
            html_content = view()
1099
1100
        packageset_text = re.compile(
1101
            '\s*' + ', '.join(sorted(unsorted_names)))
1102
        self._test_packagesets(
1103
            html_content, packageset_text, 'packagesets',
1104
            'Packagesets')
1105
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1106
    def test_view_redirects_without_feature_flag(self):
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
1107
        # If the feature flag soyuz.derived_series_ui.enabled is not set the
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1108
        # view simply redirects to the derived series.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1109
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1110
1111
        self.assertIs(
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
1112
            None, getFeatureFlag('soyuz.derived_series_ui.enabled'))
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1113
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1114
1115
        response = view.request.response
1116
        self.assertEqual(302, response.getStatus())
1117
        self.assertEqual(
1118
            canonical_url(derived_series), response.getHeader('location'))
1119
1120
    def test_label(self):
1121
        # The view label includes the names of both series.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1122
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1123
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1124
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1125
1126
        self.assertEqual(
1127
            "Source package differences between 'Derilucid' and "
1128
            "parent series 'Lucid'",
1129
            view.label)
1130
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1131
    def test_label_multiple_parents(self):
1132
        # If the series has multiple parents, the view label mentions
1133
        # the generic term 'parent series'.
1134
        derived_series, parent_series = self._createChildAndParents()
1135
1136
        view = create_initialized_view(
1137
            derived_series, '+localpackagediffs')
1138
1139
        self.assertEqual(
1140
            "Source package differences between 'Derilucid' and "
1141
            "parent series",
1142
            view.label)
1143
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1144
    def test_batch_includes_needing_attention_only(self):
1145
        # The differences attribute includes differences needing
1146
        # attention only.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1147
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1148
        current_difference = self.factory.makeDistroSeriesDifference(
1149
            derived_series=derived_series)
12742.2.2 by Gavin Panella
Fix lint.
1150
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1151
            derived_series=derived_series,
1152
            status=DistroSeriesDifferenceStatus.RESOLVED)
1153
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1154
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1155
1156
        self.assertContentEqual(
1157
            [current_difference], view.cached_differences.batch)
1158
1159
    def test_batch_includes_different_versions_only(self):
1160
        # The view contains differences of type DIFFERENT_VERSIONS only.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1161
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1162
        different_versions_diff = self.factory.makeDistroSeriesDifference(
1163
            derived_series=derived_series)
12742.2.2 by Gavin Panella
Fix lint.
1164
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1165
            derived_series=derived_series,
1166
            difference_type=(
1167
                DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
1168
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1169
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1170
1171
        self.assertContentEqual(
1172
            [different_versions_diff], view.cached_differences.batch)
1173
1174
    def test_template_includes_help_link(self):
1175
        # The help link for popup help is included.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1176
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1177
        set_derived_series_ui_feature_flag(self)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1178
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1179
1180
        soup = BeautifulSoup(view())
1181
        help_links = soup.findAll(
1182
            'a', href='/+help/soyuz/derived-series-syncing.html')
1183
        self.assertEqual(1, len(help_links))
1184
1185
    def test_diff_row_includes_last_comment_only(self):
1186
        # The most recent comment is rendered for each difference.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1187
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1188
        difference = self.factory.makeDistroSeriesDifference(
1189
            derived_series=derived_series)
12959.2.18 by Gavin Panella
Some attempts to fix up test errors/failures.
1190
        with person_logged_in(derived_series.owner):
1191
            difference.addComment(difference.owner, "Earlier comment")
1192
            difference.addComment(difference.owner, "Latest comment")
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1193
1194
        set_derived_series_ui_feature_flag(self)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1195
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1196
1197
        # Find all the rows within the body of the table
1198
        # listing the differences.
1199
        soup = BeautifulSoup(view())
1200
        diff_table = soup.find('table', {'class': 'listing'})
1201
        rows = diff_table.tbody.findAll('tr')
1202
1203
        self.assertEqual(1, len(rows))
1204
        self.assertIn("Latest comment", unicode(rows[0]))
1205
        self.assertNotIn("Earlier comment", unicode(rows[0]))
1206
1207
    def test_diff_row_links_to_extra_details(self):
1208
        # The source package name links to the difference details.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1209
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1210
        difference = self.factory.makeDistroSeriesDifference(
1211
            derived_series=derived_series)
1212
1213
        set_derived_series_ui_feature_flag(self)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1214
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1215
        soup = BeautifulSoup(view())
1216
        diff_table = soup.find('table', {'class': 'listing'})
1217
        row = diff_table.tbody.findAll('tr')[0]
1218
12959.2.28 by Gavin Panella
Fix last few test failures, phew.
1219
        links = row.findAll('a', href=canonical_url(difference))
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1220
        self.assertEqual(1, len(links))
1221
        self.assertEqual(difference.source_package_name.name, links[0].string)
1222
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1223
    def test_multiple_parents_display(self):
1224
        package_name = 'package-1'
1225
        other_parent_series = self.factory.makeDistroSeries(name='other')
1226
        derived_series, parent_series = self._createChildAndParents(
1227
            other_parent_series=other_parent_series)
1228
        versions = {
1229
            'base': u'1.0',
1230
            'derived': u'1.0derived1',
1231
            'parent': u'1.0-1',
1232
        }
1233
1234
        self.factory.makeDistroSeriesDifference(
1235
            versions=versions,
1236
            parent_series=other_parent_series,
1237
            source_package_name_str=package_name,
1238
            derived_series=derived_series)
1239
        self.factory.makeDistroSeriesDifference(
1240
            versions=versions,
1241
            parent_series=parent_series,
1242
            source_package_name_str=package_name,
1243
            derived_series=derived_series)
1244
        set_derived_series_ui_feature_flag(self)
1245
        view = create_initialized_view(
1246
            derived_series, '+localpackagediffs')
1247
        multiple_parents_matches = soupmatchers.HTMLContains(
1248
            soupmatchers.Tag(
1249
                "Parent table header", 'th',
1250
                text=re.compile("\s*Parent\s")),
1251
            soupmatchers.Tag(
1252
                "Parent version table header", 'th',
1253
                text=re.compile("\s*Parent version\s*")),
1254
            soupmatchers.Tag(
1255
                "Parent name", 'a',
1256
                attrs={'class': 'parent-name'},
1257
                text=re.compile("\s*Other\s*")),
1258
             )
1259
        self.assertThat(view.render(), multiple_parents_matches)
1260
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1261
    def test_diff_row_shows_version_attached(self):
1262
        # The +localpackagediffs page shows the version attached to the
1263
        # DSD and not the last published version (bug=745776).
1264
        package_name = 'package-1'
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1265
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1266
        versions = {
1267
            'base': u'1.0',
1268
            'derived': u'1.0derived1',
1269
            'parent': u'1.0-1',
1270
        }
1271
        new_version = u'1.2'
1272
1273
        difference = self.factory.makeDistroSeriesDifference(
1274
            versions=versions,
1275
            source_package_name_str=package_name,
1276
            derived_series=derived_series)
1277
1278
        # Create a more recent source package publishing history.
1279
        sourcepackagename = self.factory.getOrMakeSourcePackageName(
1280
            package_name)
1281
        self.factory.makeSourcePackagePublishingHistory(
1282
            sourcepackagename=sourcepackagename,
1283
            distroseries=derived_series,
1284
            version=new_version)
1285
1286
        set_derived_series_ui_feature_flag(self)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1287
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1288
        soup = BeautifulSoup(view())
1289
        diff_table = soup.find('table', {'class': 'listing'})
1290
        row = diff_table.tbody.tr
1291
        links = row.findAll('a', {'class': 'derived-version'})
1292
1293
        # The version displayed is the version attached to the
1294
        # difference.
1295
        self.assertEqual(1, len(links))
1296
        self.assertEqual(versions['derived'], links[0].string.strip())
1297
1298
        link = canonical_url(difference.source_pub.sourcepackagerelease)
1299
        self.assertTrue(link, EndsWith(new_version))
1300
        # The link points to the sourcepackagerelease referenced in the
1301
        # difference.
1302
        self.assertTrue(
1303
            links[0].get('href'), EndsWith(difference.source_version))
1304
1305
    def test_diff_row_no_published_version(self):
1306
        # The +localpackagediffs page shows only the version (no link)
1307
        # if we fail to fetch the published version.
1308
        package_name = 'package-1'
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1309
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1310
        versions = {
1311
            'base': u'1.0',
1312
            'derived': u'1.0derived1',
1313
            'parent': u'1.0-1',
1314
        }
1315
1316
        difference = self.factory.makeDistroSeriesDifference(
1317
            versions=versions,
1318
            source_package_name_str=package_name,
1319
            derived_series=derived_series)
1320
1321
        # Delete the publications.
12959.2.18 by Gavin Panella
Some attempts to fix up test errors/failures.
1322
        with celebrity_logged_in("admin"):
1323
            difference.source_pub.status = (
1324
                PackagePublishingStatus.DELETED)
1325
            difference.parent_source_pub.status = (
1326
                PackagePublishingStatus.DELETED)
12742.3.28 by Gavin Panella
Fix test_diff_row_no_published_version().
1327
        # Flush out the changes and invalidate caches (esp. property caches).
1328
        flush_database_caches()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1329
1330
        set_derived_series_ui_feature_flag(self)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1331
        view = self.makeView(derived_series)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1332
        soup = BeautifulSoup(view())
1333
        diff_table = soup.find('table', {'class': 'listing'})
1334
        row = diff_table.tbody.tr
1335
1336
        # The table feature a simple span since we were unable to fetch a
1337
        # published sourcepackage.
1338
        derived_span = row.findAll('span', {'class': 'derived-version'})
1339
        parent_span = row.findAll('span', {'class': 'parent-version'})
1340
        self.assertEqual(1, len(derived_span))
1341
        self.assertEqual(1, len(parent_span))
1342
1343
        # The versions displayed are the versions attached to the
1344
        # difference.
1345
        self.assertEqual(versions['derived'], derived_span[0].string.strip())
1346
        self.assertEqual(versions['parent'], parent_span[0].string.strip())
1347
13444.6.43 by Gavin Panella
Use text_content() in test_diff_row_last_changed() for +missingpackages, and rename the tests to be about the last-changed column as a whole.
1348
    def test_diff_row_last_changed(self):
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
1349
        # The SPR creator (i.e. who make the package change, rather than the
1350
        # uploader) is shown on each difference row.
1351
        set_derived_series_ui_feature_flag(self)
1352
        dsd = self.makePackageUpgrade()
1353
        view = self.makeView(dsd.derived_series)
1354
        root = html.fromstring(view())
1355
        [creator_cell] = root.cssselect(
13444.6.39 by Gavin Panella
The column name 'Last changed by' is misleading because the first thing shown is a date.
1356
            "table.listing tbody td.last-changed")
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
1357
        self.assertEqual(
13444.6.34 by Gavin Panella
Show the uploader too.
1358
            "a moment ago by %s" % (
1359
                dsd.source_package_release.creator.displayname,),
1360
            normalize_whitespace(creator_cell.text_content()))
1361
13444.6.43 by Gavin Panella
Use text_content() in test_diff_row_last_changed() for +missingpackages, and rename the tests to be about the last-changed column as a whole.
1362
    def test_diff_row_last_changed_also_shows_uploader_if_different(self):
13444.6.34 by Gavin Panella
Show the uploader too.
1363
        # When the SPR creator and uploader are different both are named on
1364
        # each difference row.
1365
        set_derived_series_ui_feature_flag(self)
1366
        dsd = self.makePackageUpgrade()
1367
        uploader = self.factory.makePerson()
1368
        removeSecurityProxy(dsd.source_package_release).dscsigningkey = (
1369
            self.factory.makeGPGKey(uploader))
1370
        view = self.makeView(dsd.derived_series)
1371
        root = html.fromstring(view())
1372
        [creator_cell] = root.cssselect(
13444.6.39 by Gavin Panella
The column name 'Last changed by' is misleading because the first thing shown is a date.
1373
            "table.listing tbody td.last-changed")
13444.6.34 by Gavin Panella
Show the uploader too.
1374
        self.assertEqual(
1375
            "a moment ago by %s (uploaded by %s)" % (
1376
                dsd.source_package_release.creator.displayname,
1377
                dsd.source_package_release.dscsigningkey.owner.displayname),
1378
            normalize_whitespace(creator_cell.text_content()))
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
1379
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1380
    def test_getUpgrades_shows_updates_in_parent(self):
1381
        # The view's getUpgrades methods lists packages that can be
1382
        # trivially upgraded: changed in the parent, not changed in the
1383
        # derived series, but present in both.
1384
        dsd = self.makePackageUpgrade()
1385
        view = self.makeView(dsd.derived_series)
1386
        self.assertContentEqual([dsd], view.getUpgrades())
1387
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1388
    def enableDerivedSeriesSyncFeature(self):
1389
        self.useFixture(
1390
            FeatureFixture(
7675.1155.1 by Gavin Panella
Rename soyuz.derived-series-(sync|ui).enabled flags to soyuz.derived_series_(sync|ui).enabled.
1391
                {u'soyuz.derived_series_sync.enabled': u'on'}))
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1392
12959.2.25 by Gavin Panella
Test to demonstrate efficacy of feature flag.
1393
    @with_celebrity_logged_in("admin")
1394
    def test_upgrades_offered_only_with_feature_flag(self):
1395
        # The "Upgrade Packages" button will only be shown when a specific
1396
        # feature flag is enabled.
1397
        view = self.makeView()
1398
        self.makePackageUpgrade(view.context)
1399
        self.assertFalse(view.canUpgrade())
1400
        self.enableDerivedSeriesSyncFeature()
1401
        self.assertTrue(view.canUpgrade())
1402
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1403
    def test_upgrades_are_offered_if_appropriate(self):
12959.2.25 by Gavin Panella
Test to demonstrate efficacy of feature flag.
1404
        # The "Upgrade Packages" button will only be shown to privileged
1405
        # users.
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1406
        self.enableDerivedSeriesSyncFeature()
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1407
        dsd = self.makePackageUpgrade()
1408
        view = self.makeView(dsd.derived_series)
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
1409
        with celebrity_logged_in("admin"):
1410
            self.assertTrue(view.canUpgrade())
1411
        with person_logged_in(self.factory.makePerson()):
1412
            self.assertFalse(view.canUpgrade())
1413
        with anonymous_logged_in():
1414
            self.assertFalse(view.canUpgrade())
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1415
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
1416
    @with_celebrity_logged_in("admin")
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1417
    def test_upgrades_offered_only_if_available(self):
1418
        # If there are no upgrades, the "Upgrade Packages" button won't
1419
        # be shown.
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1420
        self.enableDerivedSeriesSyncFeature()
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1421
        view = self.makeView()
1422
        self.assertFalse(view.canUpgrade())
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1423
        self.makePackageUpgrade(view.context)
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
1424
        self.assertTrue(view.canUpgrade())
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1425
12959.2.15 by Gavin Panella
Check permission for upgrades with a PackageUploadQueue.
1426
    @with_celebrity_logged_in("admin")
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1427
    def test_upgrades_not_offered_after_feature_freeze(self):
1428
        # There won't be an "Upgrade Packages" button once feature
1429
        # freeze has occurred.  Mass updates would not make sense after
1430
        # that point.
12959.2.24 by Gavin Panella
Enable the soyuz.derived-series-sync.enabled flag, and fix a broken test.
1431
        self.enableDerivedSeriesSyncFeature()
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1432
        upgradeable = {}
1433
        for status in SeriesStatus.items:
1434
            dsd = self.makePackageUpgrade()
1435
            dsd.derived_series.status = status
1436
            view = self.makeView(dsd.derived_series)
1437
            upgradeable[status] = view.canUpgrade()
1438
        expected = {
1439
            SeriesStatus.FUTURE: True,
1440
            SeriesStatus.EXPERIMENTAL: True,
1441
            SeriesStatus.DEVELOPMENT: True,
1442
            SeriesStatus.FROZEN: False,
1443
            SeriesStatus.CURRENT: False,
1444
            SeriesStatus.SUPPORTED: False,
1445
            SeriesStatus.OBSOLETE: False,
1446
        }
1447
        self.assertEqual(expected, upgradeable)
1448
1449
    def test_upgrade_creates_sync_jobs(self):
1450
        # requestUpgrades generates PackageCopyJobs for the upgrades
1451
        # that need doing.
1452
        dsd = self.makePackageUpgrade()
1453
        series = dsd.derived_series
13168.4.1 by Julian Edwards
Mass-sync upgrades should copy to the RELEASE pocket
1454
        with celebrity_logged_in('admin'):
1455
            series.status = SeriesStatus.DEVELOPMENT
1456
            series.datereleased = UTC_NOW
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1457
        view = self.makeView(series)
1458
        view.requestUpgrades()
7675.1131.1 by William Grant
Fix incompatibilities between the changes in r10537 and r10538.
1459
        job_source = getUtility(IPlainPackageCopyJobSource)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1460
        jobs = list(
1461
            job_source.getActiveJobs(series.distribution.main_archive))
1462
        self.assertEquals(1, len(jobs))
1463
        job = jobs[0]
7675.1131.1 by William Grant
Fix incompatibilities between the changes in r10537 and r10538.
1464
        self.assertEquals(series, job.target_distroseries)
7675.1176.1 by Jeroen Vermeulen
Move PackageCopyJob package name into db column, add copy_policy.
1465
        self.assertEqual(dsd.source_package_name.name, job.package_name)
1466
        self.assertEqual(dsd.parent_source_version, job.package_version)
13168.4.1 by Julian Edwards
Mass-sync upgrades should copy to the RELEASE pocket
1467
        self.assertEqual(PackagePublishingPocket.RELEASE, job.target_pocket)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1468
12959.2.18 by Gavin Panella
Some attempts to fix up test errors/failures.
1469
    def test_upgrade_gives_feedback(self):
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1470
        # requestUpgrades doesn't instantly perform package upgrades,
1471
        # but it shows the user a notice that the upgrades have been
1472
        # requested.
1473
        dsd = self.makePackageUpgrade()
1474
        view = self.makeView(dsd.derived_series)
1475
        view.requestUpgrades()
12959.2.18 by Gavin Panella
Some attempts to fix up test errors/failures.
1476
        expected = {
1477
            "level": BrowserNotificationLevel.INFO,
1478
            "message":
1479
                ("Upgrades of {0.displayname} packages have been "
1480
                 "requested. Please give Launchpad some time to "
1481
                 "complete these.").format(dsd.derived_series),
1482
            }
1483
        observed = map(vars, view.request.response.notifications)
1484
        self.assertEqual([expected], observed)
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1485
7675.1190.5 by Jeroen Vermeulen
Typo in test name.
1486
    def test_requestUpgrades_is_efficient(self):
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1487
        # A single web request may need to schedule large numbers of
1488
        # package upgrades.  It must do so without issuing large numbers
1489
        # of database queries.
7675.1136.2 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1490
        derived_series, parent_series = self._createChildAndParent()
12959.2.21 by Gavin Panella
Test to demonstrate that requestUpgrade() is efficient with the database.
1491
        # Take a baseline measure of queries.
1492
        self.makePackageUpgrade(derived_series=derived_series)
1493
        flush_database_caches()
1494
        with StormStatementRecorder() as recorder1:
1495
            self.makeView(derived_series).requestUpgrades()
7675.1190.6 by Jeroen Vermeulen
Fixed up test, satisfied it.
1496
        self.assertThat(recorder1, HasQueryCount(LessThan(12)))
7675.1190.4 by Jeroen Vermeulen
Test new query-count goal for requestUpgrade.
1497
7675.1190.6 by Jeroen Vermeulen
Fixed up test, satisfied it.
1498
        # The query count does not increase with the number of upgrades.
7675.1190.4 by Jeroen Vermeulen
Test new query-count goal for requestUpgrade.
1499
        for index in xrange(3):
12959.2.21 by Gavin Panella
Test to demonstrate that requestUpgrade() is efficient with the database.
1500
            self.makePackageUpgrade(derived_series=derived_series)
1501
        flush_database_caches()
1502
        with StormStatementRecorder() as recorder2:
1503
            self.makeView(derived_series).requestUpgrades()
7675.1166.11 by Jeroen Vermeulen
Alas, creating jobs does add to the query count.
1504
        self.assertThat(
1505
            recorder2,
7675.1190.6 by Jeroen Vermeulen
Fixed up test, satisfied it.
1506
            HasQueryCount(Equals(recorder1.count)))
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
1507
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
1508
    def makeDSDJob(self, dsd):
1509
        """Create a `DistroSeriesDifferenceJob` to update `dsd`."""
1510
        job_source = getUtility(IDistroSeriesDifferenceJobSource)
1511
        jobs = job_source.createForPackagePublication(
1512
            dsd.derived_series, dsd.source_package_name,
1513
            PackagePublishingPocket.RELEASE)
1514
        return jobs[0]
1515
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1516
    def test_higher_radio_mentions_parent(self):
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1517
        # The user is shown an option to display only the blacklisted
1518
        # package with a higer version than in the parent.
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1519
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1520
        derived_series, parent_series = self._createChildAndParent()
12742.2.2 by Gavin Panella
Fix lint.
1521
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1522
            derived_series=derived_series,
1523
            source_package_name_str="my-src-package")
1524
        view = create_initialized_view(
1525
            derived_series,
1526
            '+localpackagediffs')
1527
1528
        radio_title = \
13063.3.4 by Julian Edwards
fix remaining tests
1529
            " Ignored packages with a higher version than in 'Lucid'"
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1530
        radio_option_matches = soupmatchers.HTMLContains(
1531
            soupmatchers.Tag(
1532
                "radio displays parent's name", 'label',
1533
                text=radio_title),
1534
            )
1535
        self.assertThat(view.render(), radio_option_matches)
1536
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1537
    def test_higher_radio_mentions_parents(self):
1538
        set_derived_series_ui_feature_flag(self)
1539
        derived_series, parent_series = self._createChildAndParents()
1540
        self.factory.makeDistroSeriesDifference(
1541
            derived_series=derived_series,
1542
            source_package_name_str="my-src-package")
1543
        view = create_initialized_view(
1544
            derived_series,
1545
            '+localpackagediffs')
1546
1547
        radio_title = \
7675.1142.1 by William Grant
s/Blacklisted/Ignored/ in new test.
1548
            " Ignored packages with a higher version than in parent"
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1549
        radio_option_matches = soupmatchers.HTMLContains(
1550
            soupmatchers.Tag(
1551
                "radio displays parent's name", 'label',
1552
                text=radio_title),
1553
            )
1554
        self.assertThat(view.render(), radio_option_matches)
1555
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1556
    def _set_source_selection(self, series):
1557
        # Set up source package format selection so that copying will
1558
        # work with the default dsc_format used in
1559
        # makeSourcePackageRelease.
1560
        getUtility(ISourcePackageFormatSelectionSet).add(
1561
            series, SourcePackageFormat.FORMAT_1_0)
1562
1563
    def test_batch_filtered(self):
1564
        # The name_filter parameter allows filtering of packages by name.
1565
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1566
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1567
        diff1 = self.factory.makeDistroSeriesDifference(
1568
            derived_series=derived_series,
1569
            source_package_name_str="my-src-package")
1570
        diff2 = self.factory.makeDistroSeriesDifference(
1571
            derived_series=derived_series,
1572
            source_package_name_str="my-second-src-package")
1573
1574
        filtered_view = create_initialized_view(
1575
            derived_series,
1576
            '+localpackagediffs',
1577
            query_string='field.name_filter=my-src-package')
1578
        unfiltered_view = create_initialized_view(
1579
            derived_series,
1580
            '+localpackagediffs')
1581
1582
        self.assertContentEqual(
1583
            [diff1], filtered_view.cached_differences.batch)
1584
        self.assertContentEqual(
1585
            [diff2, diff1], unfiltered_view.cached_differences.batch)
1586
1587
    def test_batch_non_blacklisted(self):
1588
        # The default filter is all non blacklisted differences.
1589
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1590
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1591
        diff1 = self.factory.makeDistroSeriesDifference(
1592
            derived_series=derived_series,
1593
            source_package_name_str="my-src-package")
1594
        diff2 = self.factory.makeDistroSeriesDifference(
1595
            derived_series=derived_series,
1596
            source_package_name_str="my-second-src-package")
12742.2.2 by Gavin Panella
Fix lint.
1597
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1598
            derived_series=derived_series,
1599
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
1600
1601
        filtered_view = create_initialized_view(
1602
            derived_series,
1603
            '+localpackagediffs',
13063.3.4 by Julian Edwards
fix remaining tests
1604
            query_string='field.package_type=%s' % NON_IGNORED)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1605
        filtered_view2 = create_initialized_view(
1606
            derived_series,
1607
            '+localpackagediffs')
1608
1609
        self.assertContentEqual(
1610
            [diff2, diff1], filtered_view.cached_differences.batch)
1611
        self.assertContentEqual(
1612
            [diff2, diff1], filtered_view2.cached_differences.batch)
1613
13543.2.1 by Raphael Badin
Add a new 'All packages' option to the filtering form on +localpackagediffs.
1614
    def test_batch_all_packages(self):
1615
        # field.package_type parameter allows to list all the
1616
        # differences.
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1617
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1618
        derived_series, parent_series = self._createChildAndParent()
13543.2.1 by Raphael Badin
Add a new 'All packages' option to the filtering form on +localpackagediffs.
1619
        # Create differences of all possible statuses.
1620
        diffs = []
1621
        for status in DistroSeriesDifferenceStatus.items:
1622
            diff = self.factory.makeDistroSeriesDifference(
1623
                derived_series=derived_series, status=status)
1624
            diffs.append(diff)
1625
        all_view = create_initialized_view(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1626
            derived_series,
1627
            '+localpackagediffs',
13543.2.1 by Raphael Badin
Add a new 'All packages' option to the filtering form on +localpackagediffs.
1628
            query_string='field.package_type=%s' % ALL)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1629
13543.2.1 by Raphael Badin
Add a new 'All packages' option to the filtering form on +localpackagediffs.
1630
        self.assertContentEqual(diffs, all_view.cached_differences.batch)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1631
1632
    def test_batch_blacklisted_differences_with_higher_version(self):
1633
        # field.package_type parameter allows to list only
1634
        # blacklisted differences with a child's version higher than parent's.
1635
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1636
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1637
        blacklisted_diff_higher = self.factory.makeDistroSeriesDifference(
1638
            derived_series=derived_series,
1639
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
1640
            versions={'base': '1.1', 'parent': '1.3', 'derived': '1.10'})
12742.2.2 by Gavin Panella
Fix lint.
1641
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1642
            derived_series=derived_series,
1643
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
1644
            versions={'base': '1.1', 'parent': '1.12', 'derived': '1.10'})
1645
1646
        blacklisted_view = create_initialized_view(
1647
            derived_series,
1648
            '+localpackagediffs',
1649
            query_string='field.package_type=%s' % HIGHER_VERSION_THAN_PARENT)
1650
        unblacklisted_view = create_initialized_view(
1651
            derived_series,
1652
            '+localpackagediffs')
1653
1654
        self.assertContentEqual(
1655
            [blacklisted_diff_higher],
1656
            blacklisted_view.cached_differences.batch)
1657
        self.assertContentEqual(
1658
            [], unblacklisted_view.cached_differences.batch)
1659
1660
    def test_batch_resolved_differences(self):
1661
        # Test that we can search for differences that we marked
1662
        # resolved.
1663
        set_derived_series_ui_feature_flag(self)
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
1664
        derived_series, parent_series = self._createChildAndParent()
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1665
12742.2.2 by Gavin Panella
Fix lint.
1666
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1667
            derived_series=derived_series,
1668
            source_package_name_str="my-src-package")
12742.2.2 by Gavin Panella
Fix lint.
1669
        self.factory.makeDistroSeriesDifference(
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1670
            derived_series=derived_series,
1671
            source_package_name_str="my-second-src-package")
1672
        resolved_diff = self.factory.makeDistroSeriesDifference(
1673
            derived_series=derived_series,
1674
            status=DistroSeriesDifferenceStatus.RESOLVED)
1675
1676
        filtered_view = create_initialized_view(
1677
            derived_series,
1678
            '+localpackagediffs',
1679
            query_string='field.package_type=%s' % RESOLVED)
1680
1681
        self.assertContentEqual(
1682
            [resolved_diff], filtered_view.cached_differences.batch)
1683
12981.1.2 by Raphael Badin
Roll back 12977.
1684
    def _setUpDSD(self, src_name='src-name', versions=None,
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1685
                  difference_type=None, distribution=None):
12981.1.2 by Raphael Badin
Roll back 12977.
1686
        # Helper to create a derived series with fixed names and proper
1687
        # source package format selection along with a DSD.
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
1688
        parent_series = self.factory.makeDistroSeries(name='warty')
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1689
        if distribution == None:
1690
            distribution = self.factory.makeDistribution('deribuntu')
12979.1.1 by William Grant
Revive _setUpDSD and _syncAndGetView. They were reverted as part of r12960, but r12970 uses them.
1691
        derived_series = self.factory.makeDistroSeries(
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1692
            distribution=distribution,
7675.1123.11 by Steve Kowalik
Add a DSP call into _setUpDSD
1693
            name='derilucid')
1694
        self.factory.makeDistroSeriesParent(
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
1695
            derived_series=derived_series, parent_series=parent_series)
12981.1.2 by Raphael Badin
Roll back 12977.
1696
        self._set_source_selection(derived_series)
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1697
        diff = self.factory.makeDistroSeriesDifference(
12981.1.2 by Raphael Badin
Roll back 12977.
1698
            source_package_name_str=src_name,
1699
            derived_series=derived_series, versions=versions,
1700
            difference_type=difference_type)
1701
        sourcepackagename = self.factory.getOrMakeSourcePackageName(
1702
            src_name)
12979.1.1 by William Grant
Revive _setUpDSD and _syncAndGetView. They were reverted as part of r12960, but r12970 uses them.
1703
        set_derived_series_ui_feature_flag(self)
7675.1149.3 by Gavin Panella
Merge parent-to-previous-bug-782105-take-2, resolving a few conflicts.
1704
        return derived_series, parent_series, sourcepackagename, str(diff.id)
12981.1.2 by Raphael Badin
Roll back 12977.
1705
1706
    def test_canPerformSync_anon(self):
1707
        # Anonymous users cannot sync packages.
12981.1.5 by Raphael Badin
Fix as per MP's comments.
1708
        derived_series = self._setUpDSD()[0]
12981.1.2 by Raphael Badin
Roll back 12977.
1709
        view = create_initialized_view(
1710
            derived_series, '+localpackagediffs')
1711
1712
        self.assertFalse(view.canPerformSync())
1713
1714
    def test_canPerformSync_non_anon_no_perm_dest_archive(self):
1715
        # Logged-in users with no permission on the destination archive
1716
        # are not presented with options to perform syncs.
12981.1.5 by Raphael Badin
Fix as per MP's comments.
1717
        derived_series = self._setUpDSD()[0]
12976.1.1 by William Grant
Revert r12960. It breaks syncSource for ubuntu-security.
1718
        with person_logged_in(self.factory.makePerson()):
1719
            view = create_initialized_view(
1720
                derived_series, '+localpackagediffs')
12927.2.20 by Raphael Badin
Only show sync button to users with a permission on the destination archive.
1721
12981.1.2 by Raphael Badin
Roll back 12977.
1722
            self.assertFalse(view.canPerformSync())
1723
1724
    def _setUpPersonWithPerm(self, derived_series):
1725
        # Helper to create a person with an upload permission on the
1726
        # series' archive.
1727
        person = self.factory.makePerson()
1728
        ArchivePermission(
1729
            archive=derived_series.main_archive, person=person,
1730
            component=getUtility(IComponentSet)["main"],
1731
            permission=ArchivePermissionType.QUEUE_ADMIN)
1732
        return person
1733
1734
    def test_canPerformSync_non_anon(self):
1735
        # Logged-in users with a permission on the destination archive
1736
        # are presented with options to perform syncs.
1737
        # Note that a more fine-grained perm check is done on each
1738
        # synced package.
12981.1.5 by Raphael Badin
Fix as per MP's comments.
1739
        derived_series = self._setUpDSD()[0]
12981.1.2 by Raphael Badin
Roll back 12977.
1740
        person = self._setUpPersonWithPerm(derived_series)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1741
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
1742
        with person_logged_in(person):
12976.1.1 by William Grant
Revert r12960. It breaks syncSource for ubuntu-security.
1743
            view = create_initialized_view(
1744
                derived_series, '+localpackagediffs')
12981.1.2 by Raphael Badin
Roll back 12977.
1745
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
1746
            self.assertTrue(view.canPerformSync())
1747
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1748
    def test_canPerformSync_non_anon_feature_disabled(self):
1749
        # Logged-in users with a permission on the destination archive
1750
        # are not presented with options to perform syncs when the
1751
        # feature flag is not enabled.
13001.1.2 by Julian Edwards
fix lint
1752
        self.assertIs(
13001.1.3 by Julian Edwards
rename the flag
1753
            None, getFeatureFlag('soyuz.derived_series_sync.enabled'))
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1754
        derived_series = self._setUpDSD()[0]
1755
        person = self._setUpPersonWithPerm(derived_series)
1756
        with person_logged_in(person):
1757
            view = create_initialized_view(
1758
                derived_series, '+localpackagediffs')
1759
1760
            self.assertFalse(view.canPerformSync())
1761
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
1762
    def test_hasPendingDSDUpdate_returns_False_if_no_pending_update(self):
1763
        dsd = self.factory.makeDistroSeriesDifference()
1764
        view = create_initialized_view(
1765
            dsd.derived_series, '+localpackagediffs')
1766
        self.assertFalse(view.hasPendingDSDUpdate(dsd))
1767
1768
    def test_hasPendingDSDUpdate_returns_True_if_pending_update(self):
1769
        set_derived_series_difference_jobs_feature_flag(self)
1770
        dsd = self.factory.makeDistroSeriesDifference()
1771
        self.makeDSDJob(dsd)
1772
        view = create_initialized_view(
1773
            dsd.derived_series, '+localpackagediffs')
1774
        self.assertTrue(view.hasPendingDSDUpdate(dsd))
1775
7675.1143.8 by Jeroen Vermeulen
Review changes.
1776
    def test_hasPendingSync_returns_False_if_no_pending_sync(self):
1777
        dsd = self.factory.makeDistroSeriesDifference()
1778
        view = create_initialized_view(
1779
            dsd.derived_series, '+localpackagediffs')
1780
        self.assertFalse(view.hasPendingSync(dsd))
1781
1782
    def test_hasPendingSync_returns_True_if_pending_sync(self):
1783
        dsd = self.factory.makeDistroSeriesDifference()
1784
        view = create_initialized_view(
1785
            dsd.derived_series, '+localpackagediffs')
7675.1176.1 by Jeroen Vermeulen
Move PackageCopyJob package name into db column, add copy_policy.
1786
        view.pending_syncs = {dsd.source_package_name.name: object()}
7675.1143.8 by Jeroen Vermeulen
Review changes.
1787
        self.assertTrue(view.hasPendingSync(dsd))
1788
7675.1154.2 by Jeroen Vermeulen
DSD source versions are stored as raw strings, so need to cast to Version first.
1789
    def test_isNewerThanParent_compares_versions_not_strings(self):
1790
        # isNewerThanParent compares Debian-style version numbers, not
1791
        # raw version strings.  So it's possible for a child version to
1792
        # be considered newer than the corresponding parent version even
1793
        # though a string comparison goes the other way.
1794
        versions = dict(base='1.0', parent='1.1c', derived='1.10')
1795
        dsd = self.factory.makeDistroSeriesDifference(versions=versions)
1796
        view = create_initialized_view(
1797
            dsd.derived_series, '+localpackagediffs')
1798
1799
        # Assumption for the test: the child version is greater than the
1800
        # parent version, but a string comparison puts them the other
1801
        # way around.
1802
        self.assertFalse(versions['parent'] < versions['derived'])
1803
        self.assertTrue(
1804
            Version(versions['parent']) < Version(versions['derived']))
1805
1806
        # isNewerThanParent is not fooled by the misleading string
1807
        # comparison.
1808
        self.assertTrue(view.isNewerThanParent(dsd))
1809
7675.1154.1 by Jeroen Vermeulen
Don't offer to sync packages that are newer in the derived series.
1810
    def test_isNewerThanParent_is_False_for_parent_update(self):
1811
        dsd = self.factory.makeDistroSeriesDifference(
1812
            versions=dict(base='1.0', parent='1.1', derived='1.0'))
1813
        view = create_initialized_view(
1814
            dsd.derived_series, '+localpackagediffs')
1815
        self.assertFalse(view.isNewerThanParent(dsd))
1816
1817
    def test_isNewerThanParent_is_False_for_equivalent_updates(self):
1818
        # Some non-identical version numbers compare as "equal."  If the
1819
        # child and parent versions compare as equal, the child version
1820
        # is not considered newer.
1821
        dsd = self.factory.makeDistroSeriesDifference(
1822
            versions=dict(base='1.0', parent='1.1', derived='1.1'))
1823
        view = create_initialized_view(
1824
            dsd.derived_series, '+localpackagediffs')
1825
        self.assertFalse(view.isNewerThanParent(dsd))
1826
1827
    def test_isNewerThanParent_is_True_for_child_update(self):
1828
        dsd = self.factory.makeDistroSeriesDifference(
1829
            versions=dict(base='1.0', parent='1.0', derived='1.1'))
1830
        view = create_initialized_view(
1831
            dsd.derived_series, '+localpackagediffs')
1832
        self.assertTrue(view.isNewerThanParent(dsd))
1833
7675.1143.8 by Jeroen Vermeulen
Review changes.
1834
    def test_canRequestSync_returns_False_if_pending_sync(self):
1835
        dsd = self.factory.makeDistroSeriesDifference()
1836
        view = create_initialized_view(
1837
            dsd.derived_series, '+localpackagediffs')
7675.1176.1 by Jeroen Vermeulen
Move PackageCopyJob package name into db column, add copy_policy.
1838
        view.pending_syncs = {dsd.source_package_name.name: object()}
7675.1143.8 by Jeroen Vermeulen
Review changes.
1839
        self.assertFalse(view.canRequestSync(dsd))
1840
7675.1154.1 by Jeroen Vermeulen
Don't offer to sync packages that are newer in the derived series.
1841
    def test_canRequestSync_returns_False_if_child_is_newer(self):
1842
        dsd = self.factory.makeDistroSeriesDifference(
1843
            versions=dict(base='1.0', parent='1.0', derived='1.1'))
1844
        view = create_initialized_view(
1845
            dsd.derived_series, '+localpackagediffs')
1846
        self.assertFalse(view.canRequestSync(dsd))
1847
7675.1143.8 by Jeroen Vermeulen
Review changes.
1848
    def test_canRequestSync_returns_True_if_sync_makes_sense(self):
1849
        dsd = self.factory.makeDistroSeriesDifference()
1850
        view = create_initialized_view(
1851
            dsd.derived_series, '+localpackagediffs')
1852
        self.assertTrue(view.canRequestSync(dsd))
1853
7675.1162.1 by Jeroen Vermeulen
Show updating DSDs as well as syncing ones.
1854
    def test_canRequestSync_ignores_DSDJobs(self):
1855
        dsd = self.factory.makeDistroSeriesDifference()
1856
        view = create_initialized_view(
1857
            dsd.derived_series, '+localpackagediffs')
1858
        view.hasPendingDSDUpdate = FakeMethod(result=True)
1859
        self.assertTrue(view.canRequestSync(dsd))
1860
1861
    def test_describeJobs_returns_None_if_no_jobs(self):
1862
        dsd = self.factory.makeDistroSeriesDifference()
1863
        view = create_initialized_view(
1864
            dsd.derived_series, '+localpackagediffs')
1865
        self.assertIs(None, view.describeJobs(dsd))
1866
1867
    def test_describeJobs_reports_pending_update(self):
1868
        dsd = self.factory.makeDistroSeriesDifference()
1869
        view = create_initialized_view(
1870
            dsd.derived_series, '+localpackagediffs')
1871
        view.hasPendingDSDUpdate = FakeMethod(result=True)
1872
        view.hasPendingSync = FakeMethod(result=False)
1873
        self.assertEqual("updating&hellip;", view.describeJobs(dsd))
1874
1875
    def test_describeJobs_reports_pending_sync(self):
1876
        dsd = self.factory.makeDistroSeriesDifference()
1877
        view = create_initialized_view(
1878
            dsd.derived_series, '+localpackagediffs')
1879
        view.hasPendingDSDUpdate = FakeMethod(result=False)
1880
        view.hasPendingSync = FakeMethod(result=True)
1881
        self.assertEqual("synchronizing&hellip;", view.describeJobs(dsd))
1882
1883
    def test_describeJobs_reports_pending_sync_and_update(self):
1884
        dsd = self.factory.makeDistroSeriesDifference()
1885
        view = create_initialized_view(
1886
            dsd.derived_series, '+localpackagediffs')
1887
        view.hasPendingDSDUpdate = FakeMethod(result=True)
1888
        view.hasPendingSync = FakeMethod(result=True)
1889
        self.assertEqual(
1890
            "updating and synchronizing&hellip;", view.describeJobs(dsd))
1891
12981.1.2 by Raphael Badin
Roll back 12977.
1892
    def _syncAndGetView(self, derived_series, person, sync_differences,
13278.3.1 by Raphael Badin
Sync form should self-post.
1893
                        difference_type=None, view_name='+localpackagediffs',
1894
                        query_string=''):
12981.1.2 by Raphael Badin
Roll back 12977.
1895
        # A helper to get the POST'ed sync view.
1896
        with person_logged_in(person):
1897
            view = create_initialized_view(
1898
                derived_series, view_name,
1899
                method='POST', form={
1900
                    'field.selected_differences': sync_differences,
13278.3.1 by Raphael Badin
Sync form should self-post.
1901
                    'field.actions.sync': 'Sync'},
1902
                query_string=query_string)
12981.1.2 by Raphael Badin
Roll back 12977.
1903
            return view
1904
1905
    def test_sync_error_nothing_selected(self):
1906
        # An error is raised when a sync is requested without any selection.
12981.1.5 by Raphael Badin
Fix as per MP's comments.
1907
        derived_series = self._setUpDSD()[0]
12981.1.2 by Raphael Badin
Roll back 12977.
1908
        person = self._setUpPersonWithPerm(derived_series)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1909
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
1910
        view = self._syncAndGetView(derived_series, person, [])
1911
1912
        self.assertEqual(1, len(view.errors))
1913
        self.assertEqual(
1914
            'No differences selected.', view.errors[0])
1915
1916
    def test_sync_error_invalid_selection(self):
1917
        # An error is raised when an invalid difference is selected.
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1918
        derived_series, unused, unused2, diff_id = self._setUpDSD(
1919
            'my-src-name')
12981.1.2 by Raphael Badin
Roll back 12977.
1920
        person = self._setUpPersonWithPerm(derived_series)
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1921
        another_id = str(int(diff_id) + 1)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1922
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
1923
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1924
            derived_series, person, [another_id])
12981.1.2 by Raphael Badin
Roll back 12977.
1925
1926
        self.assertEqual(2, len(view.errors))
1927
        self.assertEqual(
1928
            'No differences selected.', view.errors[0])
1929
        self.assertEqual(
1930
            'Invalid value', view.errors[1].error_name)
1931
1932
    def test_sync_error_no_perm_dest_archive(self):
1933
        # A user without upload rights on the destination archive cannot
1934
        # sync packages.
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1935
        derived_series, unused, unused2, diff_id = self._setUpDSD(
1936
            'my-src-name')
12981.1.2 by Raphael Badin
Roll back 12977.
1937
        person = self._setUpPersonWithPerm(derived_series)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1938
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
1939
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1940
            derived_series, person, [diff_id])
12981.1.2 by Raphael Badin
Roll back 12977.
1941
1942
        self.assertEqual(1, len(view.errors))
1943
        self.assertTrue(
1944
            "The signer of this package has no upload rights to this "
1945
            "distribution's primary archive" in view.errors[0])
1946
1947
    def makePersonWithComponentPermission(self, archive, component=None):
1948
        person = self.factory.makePerson()
1949
        if component is None:
1950
            component = self.factory.makeComponent()
1951
        removeSecurityProxy(archive).newComponentUploader(
1952
            person, component)
1953
        return person, component
1954
1955
    def test_sync_success_perm_component(self):
1956
        # A user with upload rights on the destination component
1957
        # can sync packages.
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
1958
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
12981.1.2 by Raphael Badin
Roll back 12977.
1959
            'my-src-name')
1960
        person, _ = self.makePersonWithComponentPermission(
1961
            derived_series.main_archive,
1962
            derived_series.getSourcePackage(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1963
                sp_name).latest_published_component)
12981.1.2 by Raphael Badin
Roll back 12977.
1964
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1965
            derived_series, person, [diff_id])
12981.1.2 by Raphael Badin
Roll back 12977.
1966
1967
        self.assertEqual(0, len(view.errors))
1968
1969
    def test_sync_error_no_perm_component(self):
1970
        # A user without upload rights on the destination component
1971
        # will get an error when he syncs packages to this component.
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
1972
        derived_series, parent_series, unused, diff_id = self._setUpDSD(
12981.1.2 by Raphael Badin
Roll back 12977.
1973
            'my-src-name')
1974
        person, another_component = self.makePersonWithComponentPermission(
1975
            derived_series.main_archive)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
1976
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
1977
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
1978
            derived_series, person, [diff_id])
12981.1.2 by Raphael Badin
Roll back 12977.
1979
1980
        self.assertEqual(1, len(view.errors))
1981
        self.assertTrue(
1982
            "Signer is not permitted to upload to the "
1983
            "component" in view.errors[0])
1984
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1985
    def assertPackageCopied(self, series, src_name, version, view):
13181.1.1 by Julian Edwards
make syncs always create a PCJ
1986
        # Helper to check that a package has been copied by virtue of
1987
        # there being a package copy job ready to run.
1988
        pcj = PlainPackageCopyJob.getActiveJobs(series.main_archive).one()
1989
        self.assertEqual(version, pcj.package_version)
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1990
1991
        # The view should show no errors, and the notification should
12927.3.12 by Raphael Badin
Typos.
1992
        # confirm the sync worked.
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1993
        self.assertEqual(0, len(view.errors))
1994
        notifications = view.request.response.notifications
1995
        self.assertEqual(1, len(notifications))
13181.1.1 by Julian Edwards
make syncs always create a PCJ
1996
        self.assertIn(
1997
            "<p>Requested sync of 1 packages.</p>",
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
1998
            notifications[0].message)
12927.3.12 by Raphael Badin
Typos.
1999
        # 302 is a redirect back to the same page.
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
2000
        self.assertEqual(302, view.request.response.getStatus())
2001
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2002
    def test_sync_notification_on_success(self):
12981.1.2 by Raphael Badin
Roll back 12977.
2003
        # A user with upload rights on the destination archive can
2004
        # sync packages. Notifications about the synced packages are
2005
        # displayed and the packages are copied inside the destination
2006
        # series.
12927.3.1 by Raphael Badin
Sync package in -updates if the destination series is a released series.
2007
        versions = {
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2008
            'base': '1.0',
2009
            'derived': '1.0derived1',
12927.3.1 by Raphael Badin
Sync package in -updates if the destination series is a released series.
2010
            'parent': '1.0-1',
2011
        }
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
2012
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
12981.1.2 by Raphael Badin
Roll back 12977.
2013
            'my-src-name', versions=versions)
2014
2015
        # Setup a user with upload rights.
2016
        person = self.factory.makePerson()
2017
        removeSecurityProxy(derived_series.main_archive).newPackageUploader(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
2018
            person, sp_name)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2019
2020
        # The inital state is that 1.0-1 is not in the derived series.
2021
        pubs = derived_series.main_archive.getPublishedSources(
2022
            name='my-src-name', version=versions['parent'],
2023
            distroseries=derived_series).any()
2024
        self.assertIs(None, pubs)
2025
2026
        # Now, sync the source from the parent using the form.
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
2027
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
2028
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
2029
            derived_series, person, [diff_id])
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2030
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
2031
        # The parent's version should now be in the derived series and
2032
        # the notifications displayed:
2033
        self.assertPackageCopied(
2034
            derived_series, 'my-src-name', versions['parent'], view)
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2035
12981.1.2 by Raphael Badin
Roll back 12977.
2036
    def test_sync_success_not_yet_in_derived_series(self):
2037
        # If the package to sync does not exist yet in the derived series,
2038
        # upload right to any component inside the destination series will be
2039
        # enough to sync the package.
2040
        versions = {
2041
            'parent': '1.0-1',
2042
        }
2043
        missing = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
2044
        derived_series, parent_series, unused, diff_id = self._setUpDSD(
12981.1.2 by Raphael Badin
Roll back 12977.
2045
            'my-src-name', difference_type=missing, versions=versions)
2046
        person, another_component = self.makePersonWithComponentPermission(
2047
            derived_series.main_archive)
13001.1.1 by Julian Edwards
Add soyuz.derived-series-sync.enabled flag to protect the sync button
2048
        set_derived_series_sync_feature_flag(self)
12981.1.2 by Raphael Badin
Roll back 12977.
2049
        view = self._syncAndGetView(
7675.1135.3 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
2050
            derived_series, person, [diff_id],
12981.1.2 by Raphael Badin
Roll back 12977.
2051
            view_name='+missingpackages')
2052
12927.2.23 by Raphael Badin
A user with lp.Append on the main_archive should be able to sync packages.
2053
        self.assertPackageCopied(
2054
            derived_series, 'my-src-name', versions['parent'], view)
2055
13013.1.1 by Raphael Badin
Fix series status check.
2056
    def test_sync_in_released_series_in_updates(self):
2057
        # If the destination series is released, the sync packages end
2058
        # up in the updates pocket.
2059
        versions = {
2060
            'parent': '1.0-1',
2061
            }
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
2062
        derived_series, parent_series, sp_name, diff_id = self._setUpDSD(
13013.1.1 by Raphael Badin
Fix series status check.
2063
            'my-src-name', versions=versions)
2064
        # Update destination series status to current and update
2065
        # daterelease.
2066
        with celebrity_logged_in('admin'):
2067
            derived_series.status = SeriesStatus.CURRENT
2068
            derived_series.datereleased = UTC_NOW
2069
13013.1.2 by Raphael Badin
Set sync feature flag in test.
2070
        set_derived_series_sync_feature_flag(self)
13013.1.1 by Raphael Badin
Fix series status check.
2071
        person = self.factory.makePerson()
2072
        removeSecurityProxy(derived_series.main_archive).newPackageUploader(
7675.1136.2 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
2073
            person, sp_name)
13013.1.1 by Raphael Badin
Fix series status check.
2074
        self._syncAndGetView(
7675.1136.2 by Raphael Badin
Add parent_series to the keys needed to get a DSD. Fix api DSD's uri.
2075
            derived_series, person, [diff_id])
13168.8.12 by Gavin Panella
Merge devel, fixing several conflicts.
2076
        parent_series.main_archive.getPublishedSources(
13013.1.1 by Raphael Badin
Fix series status check.
2077
            name='my-src-name', version=versions['parent'],
7675.1149.2 by Gavin Panella
Fix some more parent/previous mix-ups.
2078
            distroseries=parent_series).one()
13181.1.1 by Julian Edwards
make syncs always create a PCJ
2079
2080
        # We look for a PackageCopyJob with the right metadata.
2081
        pcj = PlainPackageCopyJob.getActiveJobs(
2082
            derived_series.main_archive).one()
2083
        self.assertEqual(PackagePublishingPocket.UPDATES, pcj.target_pocket)
13013.1.1 by Raphael Badin
Fix series status check.
2084
13278.3.1 by Raphael Badin
Sync form should self-post.
2085
    def test_diff_view_action_url(self):
2086
        # The difference pages have a fixed action_url so that the sync
2087
        # form self-posts.
2088
        derived_series, parent_series, unused, diff_id = self._setUpDSD(
2089
            'my-src-name')
2090
        person = self.factory.makePerson()
2091
        set_derived_series_sync_feature_flag(self)
2092
        with person_logged_in(person):
2093
            view = create_initialized_view(
2094
                derived_series, '+localpackagediffs', method='GET',
2095
                query_string='start=1&batch=1')
2096
2097
        self.assertEquals(
2098
            'http://127.0.0.1?start=1&batch=1',
2099
            view.action_url)
2100
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2101
    def test_specified_packagesets_filter_none_specified(self):
13444.6.23 by Gavin Panella
Improve the tests for specified_packagesets_filter.
2102
        # specified_packagesets_filter is None when there are no
2103
        # field.packageset parameters in the query.
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2104
        set_derived_series_ui_feature_flag(self)
2105
        dsd = self.factory.makeDistroSeriesDifference()
2106
        person = dsd.derived_series.owner
2107
        with person_logged_in(person):
2108
            view = create_initialized_view(
2109
                dsd.derived_series, '+localpackagediffs', method='GET',
2110
                query_string='')
2111
            self.assertIs(None, view.specified_packagesets_filter)
2112
2113
    def test_specified_packagesets_filter_specified(self):
13444.6.23 by Gavin Panella
Improve the tests for specified_packagesets_filter.
2114
        # specified_packagesets_filter returns a collection of Packagesets
2115
        # when there are field.packageset query parameters.
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2116
        set_derived_series_ui_feature_flag(self)
2117
        dsd = self.factory.makeDistroSeriesDifference()
2118
        person = dsd.derived_series.owner
13444.6.23 by Gavin Panella
Improve the tests for specified_packagesets_filter.
2119
        packageset1 = self.factory.makePackageset(
2120
            distroseries=dsd.derived_series)
2121
        packageset2 = self.factory.makePackageset(
2122
            distroseries=dsd.derived_series)
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2123
        with person_logged_in(person):
2124
            view = create_initialized_view(
2125
                dsd.derived_series, '+localpackagediffs', method='GET',
13444.6.23 by Gavin Panella
Improve the tests for specified_packagesets_filter.
2126
                query_string='field.packageset=%d&field.packageset=%d' % (
2127
                    packageset1.id, packageset2.id))
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2128
            self.assertContentEqual(
13444.6.23 by Gavin Panella
Improve the tests for specified_packagesets_filter.
2129
                [packageset1, packageset2],
2130
                view.specified_packagesets_filter)
13444.6.21 by Gavin Panella
New property, DistroSeriesDifferenceBaseView.specified_packagesets_filter.
2131
13444.6.24 by Gavin Panella
Filter according to the packagesets given in the request query.
2132
    def test_search_for_packagesets(self):
2133
        # If packagesets are supplied in the query the resulting batch will
2134
        # only contain packages in the given packagesets.
2135
        set_derived_series_ui_feature_flag(self)
2136
        dsd = self.factory.makeDistroSeriesDifference()
2137
        person = dsd.derived_series.owner
2138
        packageset = self.factory.makePackageset(
2139
            owner=person, distroseries=dsd.derived_series)
2140
        # The package is not in the packageset so the batch will be empty.
2141
        with person_logged_in(person):
2142
            view = create_initialized_view(
2143
                dsd.derived_series, '+localpackagediffs', method='GET',
2144
                query_string='field.packageset=%d' % packageset.id)
2145
            self.assertEqual(0, len(view.cached_differences.batch))
2146
            # The batch will contain the package once it has been added to the
2147
            # packageset.
2148
            packageset.add((dsd.source_package_name,))
2149
            view = create_initialized_view(
2150
                dsd.derived_series, '+localpackagediffs', method='GET',
2151
                query_string='field.packageset=%d' % packageset.id)
2152
            self.assertEqual(1, len(view.cached_differences.batch))
2153
13013.1.1 by Raphael Badin
Fix series status check.
2154
12742.2.1 by Gavin Panella
Fold test_series_views into test_distroseries.
2155
class TestDistroSeriesNeedsPackagesView(TestCaseWithFactory):
2156
    """Test the distroseries +needs-packaging view."""
2157
2158
    layer = LaunchpadZopelessLayer
2159
2160
    def test_cached_unlinked_packages(self):
2161
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
2162
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
2163
        view = create_initialized_view(distroseries, '+needs-packaging')
2164
        self.assertTrue(
2165
            IResultSet.providedBy(
2166
                view.cached_unlinked_packages.currentBatch().list),
2167
            '%s should batch IResultSet so that slicing will limit the '
2168
            'query' % view.cached_unlinked_packages.currentBatch().list)
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2169
2170
2171
class DistroSeriesMissingPackageDiffsTestCase(TestCaseWithFactory):
2172
    """Test the distroseries +missingpackages view."""
2173
2174
    layer = LaunchpadZopelessLayer
2175
2176
    def test_missingpackages_differences(self):
2177
        # The view fetches the differences with type
2178
        # MISSING_FROM_DERIVED_SERIES.
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
2179
        dsp = self.factory.makeDistroSeriesParent()
2180
        derived_series = dsp.derived_series
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2181
2182
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
12742.2.5 by Gavin Panella
Fix lint.
2183
        # Missing blacklisted diff.
2184
        self.factory.makeDistroSeriesDifference(
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2185
            difference_type=missing_type,
2186
            derived_series=derived_series,
2187
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
2188
2189
        missing_diff = self.factory.makeDistroSeriesDifference(
2190
            difference_type=missing_type,
2191
            derived_series=derived_series,
2192
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
2193
2194
        view = create_initialized_view(
2195
            derived_series, '+missingpackages')
2196
2197
        self.assertContentEqual(
2198
            [missing_diff], view.cached_differences.batch)
2199
2200
    def test_missingpackages_differences_empty(self):
2201
        # The view is empty if there is no differences with type
2202
        # MISSING_FROM_DERIVED_SERIES.
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
2203
        dsp = self.factory.makeDistroSeriesParent()
2204
        derived_series = dsp.derived_series
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2205
2206
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
2207
12742.2.5 by Gavin Panella
Fix lint.
2208
        # Missing diff.
2209
        self.factory.makeDistroSeriesDifference(
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2210
            difference_type=not_missing_type,
2211
            derived_series=derived_series,
2212
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
2213
2214
        view = create_initialized_view(
2215
            derived_series, '+missingpackages')
2216
2217
        self.assertContentEqual(
2218
            [], view.cached_differences.batch)
2219
7675.1154.3 by Jeroen Vermeulen
debversion.Version doesn't accept None.
2220
    def test_isNewerThanParent_is_False_if_missing_from_child(self):
2221
        # If a package is missing from the child series,
2222
        # isNewerThanParent returns False.
2223
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
2224
        dsd = self.factory.makeDistroSeriesDifference(
2225
            difference_type=missing_type)
2226
        view = create_initialized_view(dsd.derived_series, '+missingpackages')
2227
        self.assertFalse(view.isNewerThanParent(dsd))
2228
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2229
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
2230
class DistroSeriesMissingPackagesPageTestCase(TestCaseWithFactory,
2231
                                              DistroSeriesDifferenceMixin):
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2232
    """Test the distroseries +missingpackages page."""
2233
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
2234
    layer = LaunchpadFunctionalLayer
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2235
2236
    def setUp(self):
2237
        super(DistroSeriesMissingPackagesPageTestCase,
2238
              self).setUp('foo.bar@canonical.com')
2239
        set_derived_series_ui_feature_flag(self)
2240
        self.simple_user = self.factory.makePerson()
2241
2242
    def test_parent_packagesets_missingpackages(self):
2243
        # +missingpackages displays the packagesets in the parent.
2244
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
2245
        self.ds_diff = self.factory.makeDistroSeriesDifference(
2246
            difference_type=missing_type)
2247
2248
        with celebrity_logged_in('admin'):
2249
            ps = self.factory.makePackageset(
2250
                packages=[self.ds_diff.source_package_name],
7675.1123.8 by Steve Kowalik
* Fix some lint and a *lot* of code duplication in the DSD browser tests.
2251
                distroseries=self.ds_diff.parent_series)
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2252
2253
        with person_logged_in(self.simple_user):
2254
            view = create_initialized_view(
2255
                self.ds_diff.derived_series,
2256
                '+missingpackages',
2257
                principal=self.simple_user)
7675.1125.1 by Raphael Badin
Merge stable.
2258
            html_content = view()
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2259
2260
        packageset_text = re.compile('\s*' + ps.name)
2261
        self._test_packagesets(
7675.1125.1 by Raphael Badin
Merge stable.
2262
            html_content, packageset_text, 'parent-packagesets',
12959.5.1 by Jeroen Vermeulen
Package-upgrade button on +localpackagediffs page (unfinished).
2263
            'Parent packagesets')
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2264
13444.6.43 by Gavin Panella
Use text_content() in test_diff_row_last_changed() for +missingpackages, and rename the tests to be about the last-changed column as a whole.
2265
    def test_diff_row_last_changed(self):
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
2266
        # The parent SPR creator (i.e. who make the package change, rather
2267
        # than the uploader) is shown on each difference row.
2268
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
2269
        dsd = self.factory.makeDistroSeriesDifference(
2270
            difference_type=missing_type)
2271
        with person_logged_in(self.simple_user):
2272
            view = create_initialized_view(
2273
                dsd.derived_series, '+missingpackages',
2274
                principal=self.simple_user)
2275
            root = html.fromstring(view())
2276
        [creator_cell] = root.cssselect(
13444.6.39 by Gavin Panella
The column name 'Last changed by' is misleading because the first thing shown is a date.
2277
            "table.listing tbody td.last-changed")
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
2278
        self.assertEqual(
13444.6.43 by Gavin Panella
Use text_content() in test_diff_row_last_changed() for +missingpackages, and rename the tests to be about the last-changed column as a whole.
2279
            "a moment ago by %s" % (
2280
                dsd.parent_source_package_release.creator.displayname,),
2281
            normalize_whitespace(creator_cell.text_content()))
13444.6.33 by Gavin Panella
Show the SPR creator instead of the uploader in +localpackagediffs and its cousins.
2282
13444.6.43 by Gavin Panella
Use text_content() in test_diff_row_last_changed() for +missingpackages, and rename the tests to be about the last-changed column as a whole.
2283
    def test_diff_row_last_changed_also_shows_uploader_if_different(self):
13444.6.34 by Gavin Panella
Show the uploader too.
2284
        # When the SPR creator and uploader are different both are named on
2285
        # each difference row.
2286
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
2287
        dsd = self.factory.makeDistroSeriesDifference(
2288
            difference_type=missing_type)
2289
        uploader = self.factory.makePerson()
13444.6.35 by Gavin Panella
Fix lint.
2290
        naked_spr = removeSecurityProxy(dsd.parent_source_package_release)
2291
        naked_spr.dscsigningkey = self.factory.makeGPGKey(uploader)
13444.6.34 by Gavin Panella
Show the uploader too.
2292
        with person_logged_in(self.simple_user):
2293
            view = create_initialized_view(
2294
                dsd.derived_series, '+missingpackages',
2295
                principal=self.simple_user)
2296
            root = html.fromstring(view())
2297
        [creator_cell] = root.cssselect(
13444.6.39 by Gavin Panella
The column name 'Last changed by' is misleading because the first thing shown is a date.
2298
            "table.listing tbody td.last-changed")
13444.6.35 by Gavin Panella
Fix lint.
2299
        parent_spr = dsd.parent_source_package_release
13444.6.34 by Gavin Panella
Show the uploader too.
2300
        self.assertEqual(
2301
            "a moment ago by %s (uploaded by %s)" % (
13444.6.35 by Gavin Panella
Fix lint.
2302
                parent_spr.creator.displayname,
2303
                parent_spr.dscsigningkey.owner.displayname),
13444.6.34 by Gavin Panella
Show the uploader too.
2304
            normalize_whitespace(creator_cell.text_content()))
2305
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2306
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
2307
class DistroSerieUniquePackageDiffsTestCase(TestCaseWithFactory,
2308
                                            DistroSeriesDifferenceMixin):
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2309
    """Test the distroseries +uniquepackages view."""
2310
2311
    layer = LaunchpadZopelessLayer
2312
2313
    def test_uniquepackages_differences(self):
2314
        # The view fetches the differences with type
2315
        # UNIQUE_TO_DERIVED_SERIES.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
2316
        derived_series, parent_series = self._createChildAndParent()
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2317
2318
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
12742.2.5 by Gavin Panella
Fix lint.
2319
        # Missing blacklisted diff.
2320
        self.factory.makeDistroSeriesDifference(
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2321
            difference_type=missing_type,
2322
            derived_series=derived_series,
2323
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
2324
2325
        missing_diff = self.factory.makeDistroSeriesDifference(
2326
            difference_type=missing_type,
2327
            derived_series=derived_series,
2328
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
2329
2330
        view = create_initialized_view(
2331
            derived_series, '+uniquepackages')
2332
2333
        self.assertContentEqual(
2334
            [missing_diff], view.cached_differences.batch)
2335
13543.3.1 by Raphael Badin
Display the parent names for DSDs on +uniquepackages if we have multiple parents.
2336
    def test_uniquepackages_displays_parent(self):
2337
        # For a series derived from multiple parents, the parent for each
2338
        # DSD is displayed; no parent version is displayed because we're
2339
        # listing packages unique to the derived series.
2340
        set_derived_series_ui_feature_flag(self)
2341
        derived_series, parent_series = self._createChildAndParents()
2342
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
2343
        self.factory.makeDistroSeriesDifference(
2344
            difference_type=missing_type,
2345
            derived_series=derived_series,
2346
            parent_series=parent_series,
2347
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
2348
        view = create_initialized_view(
2349
            derived_series, '+uniquepackages')
2350
2351
        multiple_parents_display_matcher = soupmatchers.HTMLContains(
2352
            soupmatchers.Tag(
2353
                "Parent table header", 'th',
13543.3.2 by Raphael Badin
Anchor string testing.
2354
                text=re.compile("^\s*Parent\s")),
13543.3.1 by Raphael Badin
Display the parent names for DSDs on +uniquepackages if we have multiple parents.
2355
            Not(soupmatchers.Tag(
2356
                "Parent version table header", 'th',
2357
                text=re.compile("\s*Parent version\s*"))),
2358
            soupmatchers.Tag(
2359
                "Parent name", 'a',
2360
                attrs={'class': 'parent-name'},
2361
                text=re.compile("\s*%s\s*" % parent_series.displayname)),
2362
             )
2363
        self.assertThat(view.render(), multiple_parents_display_matcher)
2364
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2365
    def test_uniquepackages_differences_empty(self):
2366
        # The view is empty if there is no differences with type
2367
        # UNIQUE_TO_DERIVED_SERIES.
7675.1135.1 by Raphael Badin
Fix derivation portlet and difference pages (series multiple parents).
2368
        derived_series, parent_series = self._createChildAndParent()
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2369
2370
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
2371
12742.2.5 by Gavin Panella
Fix lint.
2372
        # Missing diff.
2373
        self.factory.makeDistroSeriesDifference(
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2374
            difference_type=not_missing_type,
2375
            derived_series=derived_series,
2376
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
2377
2378
        view = create_initialized_view(
2379
            derived_series, '+uniquepackages')
2380
2381
        self.assertContentEqual(
2382
            [], view.cached_differences.batch)
2383
7675.1154.3 by Jeroen Vermeulen
debversion.Version doesn't accept None.
2384
    def test_isNewerThanParent_is_True_if_unique_to_child(self):
2385
        unique_to_child = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
2386
        dsd = self.factory.makeDistroSeriesDifference(
2387
            difference_type=unique_to_child)
2388
        view = create_initialized_view(
2389
            dsd.derived_series, '+localpackagediffs')
2390
        self.assertTrue(view.isNewerThanParent(dsd))
2391
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2392
7675.1136.3 by Raphael Badin
Fix code duplication (bad merge). Fix class ordering.
2393
class DistroSeriesUniquePackagesPageTestCase(TestCaseWithFactory,
2394
                                             DistroSeriesDifferenceMixin):
12742.2.4 by Gavin Panella
Resurrect several tests that were lost in the merge.
2395
    """Test the distroseries +uniquepackages page."""
2396
2397
    layer = DatabaseFunctionalLayer
2398
2399
    def setUp(self):
2400
        super(DistroSeriesUniquePackagesPageTestCase,
2401
              self).setUp('foo.bar@canonical.com')
2402
        set_derived_series_ui_feature_flag(self)
2403
        self.simple_user = self.factory.makePerson()
2404
2405
    def test_packagesets_uniquepackages(self):
2406
        # +uniquepackages displays the packagesets in the parent.
2407
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
2408
        self.ds_diff = self.factory.makeDistroSeriesDifference(
2409
            difference_type=missing_type)
2410
2411
        with celebrity_logged_in('admin'):
2412
            ps = self.factory.makePackageset(
2413
                packages=[self.ds_diff.source_package_name],
2414
                distroseries=self.ds_diff.derived_series)
2415
2416
        with person_logged_in(self.simple_user):
2417
            view = create_initialized_view(
2418
                self.ds_diff.derived_series,
2419
                '+uniquepackages',
2420
                principal=self.simple_user)
2421
            html = view()
2422
2423
        packageset_text = re.compile('\s*' + ps.name)
2424
        self._test_packagesets(
2425
            html, packageset_text, 'packagesets', 'Packagesets')