~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/browser/tests/test_distroseries.py

  • Committer: Ian Booth
  • Date: 2011-04-19 15:10:57 UTC
  • mfrom: (12868 devel)
  • mto: This revision was merged to the branch mainline in revision 12983.
  • Revision ID: ian.booth@canonical.com-20110419151057-he56y6k29c4zeiyk
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 
6
6
__metaclass__ = type
7
7
 
 
8
import difflib
 
9
import re
 
10
from textwrap import TextWrapper
 
11
 
 
12
from BeautifulSoup import BeautifulSoup
8
13
from lxml import html
 
14
import soupmatchers
 
15
from storm.zope.interfaces import IResultSet
 
16
from testtools.content import (
 
17
    Content,
 
18
    text_content,
 
19
    )
 
20
from testtools.content_type import UTF8_TEXT
 
21
from testtools.matchers import (
 
22
    EndsWith,
 
23
    LessThan,
 
24
    Not,
 
25
    )
 
26
from zope.component import getUtility
 
27
from zope.security.proxy import removeSecurityProxy
9
28
 
10
 
from canonical.testing.layers import DatabaseFunctionalLayer
 
29
from canonical.config import config
 
30
from canonical.database.sqlbase import flush_database_caches
 
31
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
 
32
from canonical.launchpad.testing.pages import find_tag_by_id
 
33
from canonical.launchpad.webapp.batching import BatchNavigator
 
34
from canonical.launchpad.webapp.publisher import canonical_url
 
35
from canonical.testing.layers import (
 
36
    DatabaseFunctionalLayer,
 
37
    LaunchpadFunctionalLayer,
 
38
    LaunchpadZopelessLayer,
 
39
    )
 
40
from lp.registry.browser.distroseries import (
 
41
    BLACKLISTED,
 
42
    HIGHER_VERSION_THAN_PARENT,
 
43
    NON_BLACKLISTED,
 
44
    RESOLVED,
 
45
    )
 
46
from lp.registry.enum import (
 
47
    DistroSeriesDifferenceStatus,
 
48
    DistroSeriesDifferenceType,
 
49
    )
 
50
from lp.services.features import (
 
51
    get_relevant_feature_controller,
 
52
    getFeatureFlag,
 
53
    install_feature_controller,
 
54
    )
 
55
from lp.services.features.flags import FeatureController
 
56
from lp.services.features.model import (
 
57
    FeatureFlag,
 
58
    getFeatureStore,
 
59
    )
 
60
from lp.soyuz.enums import (
 
61
    PackagePublishingStatus,
 
62
    SourcePackageFormat,
 
63
    )
 
64
from lp.soyuz.interfaces.distributionjob import (
 
65
    IInitialiseDistroSeriesJobSource,
 
66
    )
 
67
from lp.soyuz.interfaces.sourcepackageformat import (
 
68
    ISourcePackageFormatSelectionSet,
 
69
    )
11
70
from lp.testing import (
 
71
    celebrity_logged_in,
12
72
    feature_flags,
 
73
    login_person,
13
74
    person_logged_in,
14
75
    set_feature_flag,
 
76
    StormStatementRecorder,
15
77
    TestCaseWithFactory,
16
78
    )
 
79
from lp.testing.matchers import HasQueryCount
17
80
from lp.testing.views import create_initialized_view
18
81
 
19
82
 
 
83
def set_derived_series_ui_feature_flag(test_case):
 
84
    # Helper to set the feature flag enabling the derived series ui.
 
85
    getFeatureStore().add(FeatureFlag(
 
86
        scope=u'default', flag=u'soyuz.derived-series-ui.enabled',
 
87
        value=u'on', priority=1))
 
88
 
 
89
    # XXX Michael Nelson 2010-09-21 bug=631884
 
90
    # Currently LaunchpadTestRequest doesn't set per-thread
 
91
    # features.
 
92
    def in_scope(value):
 
93
        return True
 
94
    install_feature_controller(FeatureController(in_scope))
 
95
    test_case.addCleanup(install_feature_controller, None)
 
96
 
 
97
 
 
98
class TestDistroSeriesView(TestCaseWithFactory):
 
99
    """Test the distroseries +index view."""
 
100
 
 
101
    layer = LaunchpadZopelessLayer
 
102
 
 
103
    def test_needs_linking(self):
 
104
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
 
105
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
 
106
        view = create_initialized_view(distroseries, '+index')
 
107
        self.assertEqual(view.needs_linking, None)
 
108
 
 
109
    def _createDifferenceAndGetView(self, difference_type):
 
110
        # Helper function to create a valid DSD.
 
111
        distroseries = self.factory.makeDistroSeries(
 
112
            parent_series=self.factory.makeDistroSeries())
 
113
        ds_diff = self.factory.makeDistroSeriesDifference(
 
114
            derived_series=distroseries, difference_type=difference_type)
 
115
        return create_initialized_view(distroseries, '+index')
 
116
 
 
117
    def test_num_differences(self):
 
118
        diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
 
119
        view = self._createDifferenceAndGetView(diff_type)
 
120
        self.assertEqual(1, view.num_differences)
 
121
 
 
122
    def test_num_differences_in_parent(self):
 
123
        diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
 
124
        view = self._createDifferenceAndGetView(diff_type)
 
125
        self.assertEqual(1, view.num_differences_in_parent)
 
126
 
 
127
    def test_num_differences_in_child(self):
 
128
        diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
 
129
        view = self._createDifferenceAndGetView(diff_type)
 
130
        self.assertEqual(1, view.num_differences_in_child)
 
131
 
 
132
 
 
133
class DistroSeriesIndexFunctionalTestCase(TestCaseWithFactory):
 
134
    """Test the distroseries +index page."""
 
135
 
 
136
    layer = DatabaseFunctionalLayer
 
137
 
 
138
    def _setupDifferences(self, name, parent_name, nb_diff_versions,
 
139
                          nb_diff_child, nb_diff_parent):
 
140
        # Helper to create DSD of the different types.
 
141
        derived_series = self.factory.makeDistroSeries(
 
142
            name=name,
 
143
            parent_series=self.factory.makeDistroSeries(name=parent_name))
 
144
        self.simple_user = self.factory.makePerson()
 
145
        for i in range(nb_diff_versions):
 
146
            diff_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
 
147
            self.factory.makeDistroSeriesDifference(
 
148
                derived_series=derived_series,
 
149
                difference_type=diff_type)
 
150
        for i in range(nb_diff_child):
 
151
            diff_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
 
152
            self.factory.makeDistroSeriesDifference(
 
153
                derived_series=derived_series,
 
154
                difference_type=diff_type)
 
155
        for i in range(nb_diff_parent):
 
156
            diff_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
 
157
            self.factory.makeDistroSeriesDifference(
 
158
                derived_series=derived_series,
 
159
                difference_type=diff_type)
 
160
        return derived_series
 
161
 
 
162
    def test_differences_no_flag_no_portlet(self):
 
163
        # The portlet is not displayed if the feature flag is not enabled.
 
164
        derived_series = self._setupDifferences('deri', 'sid', 1, 2, 2)
 
165
        portlet_header = soupmatchers.HTMLContains(
 
166
            soupmatchers.Tag(
 
167
                'Derivation portlet header', 'h2',
 
168
                text='Derived from Sid'),
 
169
            )
 
170
 
 
171
        with person_logged_in(self.simple_user):
 
172
            view = create_initialized_view(
 
173
                derived_series,
 
174
                '+index',
 
175
                principal=self.simple_user)
 
176
            html = view()
 
177
 
 
178
        self.assertEqual(
 
179
            None, getFeatureFlag('soyuz.derived-series-ui.enabled'))
 
180
        self.assertThat(html, Not(portlet_header))
 
181
 
 
182
    def test_differences_portlet_all_differences(self):
 
183
        # The difference portlet shows the differences with the parent
 
184
        # series.
 
185
        set_derived_series_ui_feature_flag(self)
 
186
        derived_series = self._setupDifferences('deri', 'sid', 1, 2, 3)
 
187
        portlet_display = soupmatchers.HTMLContains(
 
188
            soupmatchers.Tag(
 
189
                'Derivation portlet header', 'h2',
 
190
                text='Derived from Sid'),
 
191
            soupmatchers.Tag(
 
192
                'Differences link', 'a',
 
193
                text=re.compile('\s*1 package with differences.\s*'),
 
194
                attrs={'href': re.compile('.*/\+localpackagediffs')}),
 
195
            soupmatchers.Tag(
 
196
                'Parent diffs link', 'a',
 
197
                text=re.compile('\s*2 packages in Sid.\s*'),
 
198
                attrs={'href': re.compile('.*/\+missingpackages')}),
 
199
            soupmatchers.Tag(
 
200
                'Child diffs link', 'a',
 
201
                text=re.compile('\s*3 packages in Deri.\s*'),
 
202
                attrs={'href': re.compile('.*/\+uniquepackages')}))
 
203
 
 
204
        with person_logged_in(self.simple_user):
 
205
            view = create_initialized_view(
 
206
                derived_series,
 
207
                '+index',
 
208
                principal=self.simple_user)
 
209
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
 
210
            # self.features to NullFeatureController.
 
211
            view.request.features = get_relevant_feature_controller()
 
212
            html = view()
 
213
 
 
214
        self.assertThat(html, portlet_display)
 
215
 
 
216
    def test_differences_portlet_no_differences(self):
 
217
        # The difference portlet displays 'No differences' if there is no
 
218
        # differences with the parent.
 
219
        set_derived_series_ui_feature_flag(self)
 
220
        derived_series = self._setupDifferences('deri', 'sid', 0, 0, 0)
 
221
        portlet_display = soupmatchers.HTMLContains(
 
222
            soupmatchers.Tag(
 
223
                'Derivation portlet header', 'h2',
 
224
                text='Derived from Sid'),
 
225
            soupmatchers.Tag(
 
226
                'Child diffs link', True,
 
227
                text=re.compile('\s*No differences\s*')),
 
228
              )
 
229
 
 
230
        with person_logged_in(self.simple_user):
 
231
            view = create_initialized_view(
 
232
                derived_series,
 
233
                '+index',
 
234
                principal=self.simple_user)
 
235
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
 
236
            # self.features to NullFeatureController.
 
237
            view.request.features = get_relevant_feature_controller()
 
238
            html = view()
 
239
 
 
240
        self.assertThat(html, portlet_display)
 
241
 
 
242
    def test_differences_portlet_initialising(self):
 
243
        # The difference portlet displays 'The series is initialising.' if
 
244
        # there is an initialising job for the series.
 
245
        set_derived_series_ui_feature_flag(self)
 
246
        derived_series = self._setupDifferences('deri', 'sid', 0, 0, 0)
 
247
        job_source = getUtility(IInitialiseDistroSeriesJobSource)
 
248
        job = job_source.create(derived_series.parent, derived_series)
 
249
        portlet_display = soupmatchers.HTMLContains(
 
250
            soupmatchers.Tag(
 
251
                'Derived series', 'h2',
 
252
                text='Derived series'),
 
253
            soupmatchers.Tag(
 
254
                'Init message', True,
 
255
                text=re.compile('\s*This series is initialising.\s*')),
 
256
              )
 
257
 
 
258
        with person_logged_in(self.simple_user):
 
259
            view = create_initialized_view(
 
260
                derived_series,
 
261
                '+index',
 
262
                principal=self.simple_user)
 
263
            # XXX rvb 2011-04-12 bug=758649: LaunchpadTestRequest overrides
 
264
            # self.features to NullFeatureController.
 
265
            view.request.features = get_relevant_feature_controller()
 
266
            html = view()
 
267
 
 
268
        self.assertTrue(derived_series.is_initialising)
 
269
        self.assertThat(html, portlet_display)
 
270
 
 
271
 
 
272
class TestMilestoneBatchNavigatorAttribute(TestCaseWithFactory):
 
273
    """Test the series.milestone_batch_navigator attribute."""
 
274
 
 
275
    layer = LaunchpadZopelessLayer
 
276
 
 
277
    def test_distroseries_milestone_batch_navigator(self):
 
278
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
 
279
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
 
280
        for name in ('a', 'b', 'c', 'd'):
 
281
            distroseries.newMilestone(name)
 
282
        view = create_initialized_view(distroseries, name='+index')
 
283
        self._check_milestone_batch_navigator(view)
 
284
 
 
285
    def test_productseries_milestone_batch_navigator(self):
 
286
        product = self.factory.makeProduct()
 
287
        for name in ('a', 'b', 'c', 'd'):
 
288
            product.development_focus.newMilestone(name)
 
289
 
 
290
        view = create_initialized_view(
 
291
            product.development_focus, name='+index')
 
292
        self._check_milestone_batch_navigator(view)
 
293
 
 
294
    def _check_milestone_batch_navigator(self, view):
 
295
        config.push('default-batch-size', """
 
296
        [launchpad]
 
297
        default_batch_size: 2
 
298
        """)
 
299
        self.assert_(
 
300
            isinstance(view.milestone_batch_navigator, BatchNavigator),
 
301
            'milestone_batch_navigator is not a BatchNavigator object: %r'
 
302
            % view.milestone_batch_navigator)
 
303
        self.assertEqual(4, view.milestone_batch_navigator.batch.total())
 
304
        expected = [
 
305
            'd',
 
306
            'c',
 
307
            ]
 
308
        milestone_names = [
 
309
            item.name
 
310
            for item in view.milestone_batch_navigator.currentBatch()]
 
311
        self.assertEqual(expected, milestone_names)
 
312
        config.pop('default-batch-size')
 
313
 
 
314
 
20
315
class TestDistroSeriesAddView(TestCaseWithFactory):
21
316
 
22
317
    layer = DatabaseFunctionalLayer
101
396
            self.assertIn(
102
397
                u"javascript-disabled",
103
398
                message.get("class").split())
 
399
 
 
400
 
 
401
class DistroSeriesDifferenceMixin:
 
402
    """A helper class for testing differences pages"""
 
403
 
 
404
    def _test_packagesets(self, html, packageset_text,
 
405
                          packageset_class, msg_text):
 
406
        parent_packagesets = soupmatchers.HTMLContains(
 
407
            soupmatchers.Tag(
 
408
                msg_text, 'td',
 
409
                attrs={'class': packageset_class},
 
410
                text=packageset_text))
 
411
 
 
412
        self.assertThat(html, parent_packagesets)
 
413
 
 
414
 
 
415
class TestDistroSeriesLocalDifferences(
 
416
    DistroSeriesDifferenceMixin, TestCaseWithFactory):
 
417
    """Test the distroseries +localpackagediffs page."""
 
418
 
 
419
    layer = DatabaseFunctionalLayer
 
420
 
 
421
    def setUp(self):
 
422
        super(TestDistroSeriesLocalDifferences,
 
423
              self).setUp('foo.bar@canonical.com')
 
424
        set_derived_series_ui_feature_flag(self)
 
425
        self.simple_user = self.factory.makePerson()
 
426
 
 
427
    def test_filter_form_if_differences(self):
 
428
        # Test that the page includes the filter form if differences
 
429
        # are present
 
430
        login_person(self.simple_user)
 
431
        derived_series = self.factory.makeDistroSeries(
 
432
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
433
                name='lucid'))
 
434
        self.factory.makeDistroSeriesDifference(
 
435
            derived_series=derived_series)
 
436
 
 
437
        view = create_initialized_view(
 
438
            derived_series, '+localpackagediffs', principal=self.simple_user)
 
439
 
 
440
        self.assertIsNot(
 
441
            None,
 
442
            find_tag_by_id(view(), 'distroseries-localdiff-search-filter'),
 
443
            "Form filter should be shown when there are differences.")
 
444
 
 
445
    def test_filter_noform_if_nodifferences(self):
 
446
        # Test that the page doesn't includes the filter form if no
 
447
        # differences are present
 
448
        login_person(self.simple_user)
 
449
        derived_series = self.factory.makeDistroSeries(
 
450
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
451
                name='lucid'))
 
452
 
 
453
        view = create_initialized_view(
 
454
            derived_series, '+localpackagediffs', principal=self.simple_user)
 
455
 
 
456
        self.assertIs(
 
457
            None,
 
458
            find_tag_by_id(view(), 'distroseries-localdiff-search-filter'),
 
459
            "Form filter should not be shown when there are no differences.")
 
460
 
 
461
    def test_parent_packagesets_localpackagediffs(self):
 
462
        # +localpackagediffs displays the parent packagesets.
 
463
        ds_diff = self.factory.makeDistroSeriesDifference()
 
464
        with celebrity_logged_in('admin'):
 
465
            ps = self.factory.makePackageset(
 
466
                packages=[ds_diff.source_package_name],
 
467
                distroseries=ds_diff.derived_series.parent_series)
 
468
 
 
469
        with person_logged_in(self.simple_user):
 
470
            view = create_initialized_view(
 
471
                ds_diff.derived_series,
 
472
                '+localpackagediffs',
 
473
                principal=self.simple_user)
 
474
            html = view()
 
475
 
 
476
        packageset_text = re.compile('\s*' + ps.name)
 
477
        self._test_packagesets(
 
478
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
 
479
 
 
480
    def test_parent_packagesets_localpackagediffs_sorts(self):
 
481
        # Multiple packagesets are sorted in a comma separated list.
 
482
        ds_diff = self.factory.makeDistroSeriesDifference()
 
483
        unsorted_names = [u"zzz", u"aaa"]
 
484
        with celebrity_logged_in('admin'):
 
485
            for name in unsorted_names:
 
486
                self.factory.makePackageset(
 
487
                    name=name,
 
488
                    packages=[ds_diff.source_package_name],
 
489
                    distroseries=ds_diff.derived_series.parent_series)
 
490
 
 
491
        with person_logged_in(self.simple_user):
 
492
            view = create_initialized_view(
 
493
                ds_diff.derived_series,
 
494
                '+localpackagediffs',
 
495
                principal=self.simple_user)
 
496
            html = view()
 
497
 
 
498
        packageset_text = re.compile(
 
499
            '\s*' + ', '.join(sorted(unsorted_names)))
 
500
        self._test_packagesets(
 
501
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
 
502
 
 
503
    def test_queries(self):
 
504
        # With no DistroSeriesDifferences the query count should be low and
 
505
        # fairly static. However, with some DistroSeriesDifferences the query
 
506
        # count will be higher, but it should remain the same no matter how
 
507
        # many differences there are.
 
508
        login_person(self.simple_user)
 
509
        derived_series = self.factory.makeDistroSeries(
 
510
            parent_series=self.factory.makeDistroSeries())
 
511
 
 
512
        def add_differences(num):
 
513
            for index in xrange(num):
 
514
                version = self.factory.getUniqueInteger()
 
515
                versions = {
 
516
                    'base': u'1.%d' % version,
 
517
                    'derived': u'1.%dderived1' % version,
 
518
                    'parent': u'1.%d-1' % version,
 
519
                    }
 
520
                dsd = self.factory.makeDistroSeriesDifference(
 
521
                    derived_series=derived_series,
 
522
                    versions=versions)
 
523
 
 
524
                # Push a base_version in... not sure how better to do it.
 
525
                removeSecurityProxy(dsd).base_version = versions["base"]
 
526
 
 
527
                # Add a couple of comments.
 
528
                self.factory.makeDistroSeriesDifferenceComment(dsd)
 
529
                self.factory.makeDistroSeriesDifferenceComment(dsd)
 
530
 
 
531
                # Update the spr, some with recipes, some with signing keys.
 
532
                # SPR.uploader references both, and the uploader is referenced
 
533
                # in the page.
 
534
                spr = dsd.source_pub.sourcepackagerelease
 
535
                if index % 2 == 0:
 
536
                    removeSecurityProxy(spr).source_package_recipe_build = (
 
537
                        self.factory.makeSourcePackageRecipeBuild(
 
538
                            sourcename=spr.sourcepackagename.name,
 
539
                            distroseries=derived_series))
 
540
                else:
 
541
                    removeSecurityProxy(spr).dscsigningkey = (
 
542
                        self.factory.makeGPGKey(owner=spr.creator))
 
543
 
 
544
        def flush_and_render():
 
545
            flush_database_caches()
 
546
            # Pull in the calling user's location so that it isn't recorded in
 
547
            # the query count; it causes the total to be fragile for no
 
548
            # readily apparent reason.
 
549
            self.simple_user.location
 
550
            with StormStatementRecorder() as recorder:
 
551
                view = create_initialized_view(
 
552
                    derived_series, '+localpackagediffs',
 
553
                    principal=self.simple_user)
 
554
                view()
 
555
            return recorder, view.cached_differences.batch.trueSize
 
556
 
 
557
        def statement_differ(rec1, rec2):
 
558
            wrapper = TextWrapper(break_long_words=False)
 
559
 
 
560
            def prepare_statements(rec):
 
561
                for statement in rec.statements:
 
562
                    for line in wrapper.wrap(statement):
 
563
                        yield line
 
564
                    yield "-" * wrapper.width
 
565
 
 
566
            def statement_diff():
 
567
                diff = difflib.ndiff(
 
568
                    list(prepare_statements(rec1)),
 
569
                    list(prepare_statements(rec2)))
 
570
                for line in diff:
 
571
                    yield "%s\n" % line
 
572
 
 
573
            return statement_diff
 
574
 
 
575
        # Render without differences and check the query count isn't silly.
 
576
        recorder1, batch_size = flush_and_render()
 
577
        self.assertThat(recorder1, HasQueryCount(LessThan(30)))
 
578
        self.addDetail(
 
579
            "statement-count-0-differences",
 
580
            text_content(u"%d" % recorder1.count))
 
581
        # Add some differences and render.
 
582
        add_differences(2)
 
583
        recorder2, batch_size = flush_and_render()
 
584
        self.addDetail(
 
585
            "statement-count-2-differences",
 
586
            text_content(u"%d" % recorder2.count))
 
587
        # Add more differences and render again.
 
588
        add_differences(2)
 
589
        recorder3, batch_size = flush_and_render()
 
590
        self.addDetail(
 
591
            "statement-count-4-differences",
 
592
            text_content(u"%d" % recorder3.count))
 
593
        # The last render should not need more queries than the previous.
 
594
        self.addDetail(
 
595
            "statement-diff", Content(
 
596
                UTF8_TEXT, statement_differ(recorder2, recorder3)))
 
597
        # Details about the number of statements per row.
 
598
        statement_count_per_row = (
 
599
            (recorder3.count - recorder1.count) / float(batch_size))
 
600
        self.addDetail(
 
601
            "statement-count-per-row-average",
 
602
            text_content(u"%.2f" % statement_count_per_row))
 
603
        # XXX: GavinPanella 2011-04-12 bug=760733: Reducing the query count
 
604
        # further needs work. Ideally this test would be along the lines of
 
605
        # recorder3.count == recorder2.count. 4 queries above the recorder2
 
606
        # count is 2 queries per difference which is not acceptable, but is
 
607
        # *far* better than without the changes introduced by landing this.
 
608
        compromise_statement_count = recorder2.count + 4
 
609
        self.assertThat(
 
610
            recorder3, HasQueryCount(
 
611
                LessThan(compromise_statement_count + 1)))
 
612
 
 
613
 
 
614
class TestDistroSeriesLocalDifferencesZopeless(TestCaseWithFactory):
 
615
    """Test the distroseries +localpackagediffs view."""
 
616
 
 
617
    layer = LaunchpadZopelessLayer
 
618
 
 
619
    def test_view_redirects_without_feature_flag(self):
 
620
        # If the feature flag soyuz.derived-series-ui.enabled is not set the
 
621
        # view simply redirects to the derived series.
 
622
        derived_series = self.factory.makeDistroSeries(
 
623
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
624
                name='lucid'))
 
625
 
 
626
        self.assertIs(
 
627
            None, getFeatureFlag('soyuz.derived-series-ui.enabled'))
 
628
        view = create_initialized_view(
 
629
            derived_series, '+localpackagediffs')
 
630
 
 
631
        response = view.request.response
 
632
        self.assertEqual(302, response.getStatus())
 
633
        self.assertEqual(
 
634
            canonical_url(derived_series), response.getHeader('location'))
 
635
 
 
636
    def test_label(self):
 
637
        # The view label includes the names of both series.
 
638
        derived_series = self.factory.makeDistroSeries(
 
639
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
640
                name='lucid'))
 
641
 
 
642
        view = create_initialized_view(
 
643
            derived_series, '+localpackagediffs')
 
644
 
 
645
        self.assertEqual(
 
646
            "Source package differences between 'Derilucid' and "
 
647
            "parent series 'Lucid'",
 
648
            view.label)
 
649
 
 
650
    def test_batch_includes_needing_attention_only(self):
 
651
        # The differences attribute includes differences needing
 
652
        # attention only.
 
653
        derived_series = self.factory.makeDistroSeries(
 
654
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
655
                name='lucid'))
 
656
        current_difference = self.factory.makeDistroSeriesDifference(
 
657
            derived_series=derived_series)
 
658
        self.factory.makeDistroSeriesDifference(
 
659
            derived_series=derived_series,
 
660
            status=DistroSeriesDifferenceStatus.RESOLVED)
 
661
 
 
662
        view = create_initialized_view(
 
663
            derived_series, '+localpackagediffs')
 
664
 
 
665
        self.assertContentEqual(
 
666
            [current_difference], view.cached_differences.batch)
 
667
 
 
668
    def test_batch_includes_different_versions_only(self):
 
669
        # The view contains differences of type DIFFERENT_VERSIONS only.
 
670
        derived_series = self.factory.makeDistroSeries(
 
671
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
672
                name='lucid'))
 
673
        different_versions_diff = self.factory.makeDistroSeriesDifference(
 
674
            derived_series=derived_series)
 
675
        self.factory.makeDistroSeriesDifference(
 
676
            derived_series=derived_series,
 
677
            difference_type=(
 
678
                DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
 
679
 
 
680
        view = create_initialized_view(
 
681
            derived_series, '+localpackagediffs')
 
682
 
 
683
        self.assertContentEqual(
 
684
            [different_versions_diff], view.cached_differences.batch)
 
685
 
 
686
    def test_template_includes_help_link(self):
 
687
        # The help link for popup help is included.
 
688
        derived_series = self.factory.makeDistroSeries(
 
689
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
690
                name='lucid'))
 
691
 
 
692
        set_derived_series_ui_feature_flag(self)
 
693
        view = create_initialized_view(
 
694
            derived_series, '+localpackagediffs')
 
695
 
 
696
        soup = BeautifulSoup(view())
 
697
        help_links = soup.findAll(
 
698
            'a', href='/+help/soyuz/derived-series-syncing.html')
 
699
        self.assertEqual(1, len(help_links))
 
700
 
 
701
    def test_diff_row_includes_last_comment_only(self):
 
702
        # The most recent comment is rendered for each difference.
 
703
        derived_series = self.factory.makeDistroSeries(
 
704
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
705
                name='lucid'))
 
706
        difference = self.factory.makeDistroSeriesDifference(
 
707
            derived_series=derived_series)
 
708
        difference.addComment(difference.owner, "Earlier comment")
 
709
        difference.addComment(difference.owner, "Latest comment")
 
710
 
 
711
        set_derived_series_ui_feature_flag(self)
 
712
        view = create_initialized_view(
 
713
            derived_series, '+localpackagediffs')
 
714
 
 
715
        # Find all the rows within the body of the table
 
716
        # listing the differences.
 
717
        soup = BeautifulSoup(view())
 
718
        diff_table = soup.find('table', {'class': 'listing'})
 
719
        rows = diff_table.tbody.findAll('tr')
 
720
 
 
721
        self.assertEqual(1, len(rows))
 
722
        self.assertIn("Latest comment", unicode(rows[0]))
 
723
        self.assertNotIn("Earlier comment", unicode(rows[0]))
 
724
 
 
725
    def test_diff_row_links_to_extra_details(self):
 
726
        # The source package name links to the difference details.
 
727
        derived_series = self.factory.makeDistroSeries(
 
728
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
729
                name='lucid'))
 
730
        difference = self.factory.makeDistroSeriesDifference(
 
731
            derived_series=derived_series)
 
732
 
 
733
        set_derived_series_ui_feature_flag(self)
 
734
        view = create_initialized_view(
 
735
            derived_series, '+localpackagediffs')
 
736
        soup = BeautifulSoup(view())
 
737
        diff_table = soup.find('table', {'class': 'listing'})
 
738
        row = diff_table.tbody.findAll('tr')[0]
 
739
 
 
740
        href = canonical_url(difference).replace('http://launchpad.dev', '')
 
741
        links = row.findAll('a', href=href)
 
742
        self.assertEqual(1, len(links))
 
743
        self.assertEqual(difference.source_package_name.name, links[0].string)
 
744
 
 
745
    def test_diff_row_shows_version_attached(self):
 
746
        # The +localpackagediffs page shows the version attached to the
 
747
        # DSD and not the last published version (bug=745776).
 
748
        package_name = 'package-1'
 
749
        derived_series = self.factory.makeDistroSeries(
 
750
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
751
                name='lucid'))
 
752
        versions = {
 
753
            'base': u'1.0',
 
754
            'derived': u'1.0derived1',
 
755
            'parent': u'1.0-1',
 
756
        }
 
757
        new_version = u'1.2'
 
758
 
 
759
        difference = self.factory.makeDistroSeriesDifference(
 
760
            versions=versions,
 
761
            source_package_name_str=package_name,
 
762
            derived_series=derived_series)
 
763
 
 
764
        # Create a more recent source package publishing history.
 
765
        sourcepackagename = self.factory.getOrMakeSourcePackageName(
 
766
            package_name)
 
767
        self.factory.makeSourcePackagePublishingHistory(
 
768
            sourcepackagename=sourcepackagename,
 
769
            distroseries=derived_series,
 
770
            version=new_version)
 
771
 
 
772
        set_derived_series_ui_feature_flag(self)
 
773
        view = create_initialized_view(
 
774
            derived_series, '+localpackagediffs')
 
775
        soup = BeautifulSoup(view())
 
776
        diff_table = soup.find('table', {'class': 'listing'})
 
777
        row = diff_table.tbody.tr
 
778
        links = row.findAll('a', {'class': 'derived-version'})
 
779
 
 
780
        # The version displayed is the version attached to the
 
781
        # difference.
 
782
        self.assertEqual(1, len(links))
 
783
        self.assertEqual(versions['derived'], links[0].string.strip())
 
784
 
 
785
        link = canonical_url(difference.source_pub.sourcepackagerelease)
 
786
        self.assertTrue(link, EndsWith(new_version))
 
787
        # The link points to the sourcepackagerelease referenced in the
 
788
        # difference.
 
789
        self.assertTrue(
 
790
            links[0].get('href'), EndsWith(difference.source_version))
 
791
 
 
792
    def test_diff_row_no_published_version(self):
 
793
        # The +localpackagediffs page shows only the version (no link)
 
794
        # if we fail to fetch the published version.
 
795
        package_name = 'package-1'
 
796
        derived_series = self.factory.makeDistroSeries(
 
797
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
798
                name='lucid'))
 
799
        versions = {
 
800
            'base': u'1.0',
 
801
            'derived': u'1.0derived1',
 
802
            'parent': u'1.0-1',
 
803
        }
 
804
 
 
805
        difference = self.factory.makeDistroSeriesDifference(
 
806
            versions=versions,
 
807
            source_package_name_str=package_name,
 
808
            derived_series=derived_series)
 
809
 
 
810
        # Delete the publications.
 
811
        difference.source_pub.status = PackagePublishingStatus.DELETED
 
812
        difference.parent_source_pub.status = PackagePublishingStatus.DELETED
 
813
        # Flush out the changes and invalidate caches (esp. property caches).
 
814
        flush_database_caches()
 
815
 
 
816
        set_derived_series_ui_feature_flag(self)
 
817
        view = create_initialized_view(
 
818
            derived_series, '+localpackagediffs')
 
819
        soup = BeautifulSoup(view())
 
820
        diff_table = soup.find('table', {'class': 'listing'})
 
821
        row = diff_table.tbody.tr
 
822
 
 
823
        # The table feature a simple span since we were unable to fetch a
 
824
        # published sourcepackage.
 
825
        derived_span = row.findAll('span', {'class': 'derived-version'})
 
826
        parent_span = row.findAll('span', {'class': 'parent-version'})
 
827
        self.assertEqual(1, len(derived_span))
 
828
        self.assertEqual(1, len(parent_span))
 
829
 
 
830
        # The versions displayed are the versions attached to the
 
831
        # difference.
 
832
        self.assertEqual(versions['derived'], derived_span[0].string.strip())
 
833
        self.assertEqual(versions['parent'], parent_span[0].string.strip())
 
834
 
 
835
 
 
836
class TestDistroSeriesLocalDifferencesFunctional(TestCaseWithFactory):
 
837
 
 
838
    layer = LaunchpadFunctionalLayer
 
839
 
 
840
    def test_higher_radio_mentions_parent(self):
 
841
        set_derived_series_ui_feature_flag(self)
 
842
        parent_series = self.factory.makeDistroSeries(
 
843
            name='lucid', displayname='Lucid')
 
844
        derived_series = self.factory.makeDistroSeries(
 
845
            name='derilucid', parent_series=parent_series)
 
846
        self.factory.makeDistroSeriesDifference(
 
847
            derived_series=derived_series,
 
848
            source_package_name_str="my-src-package")
 
849
        view = create_initialized_view(
 
850
            derived_series,
 
851
            '+localpackagediffs')
 
852
 
 
853
        radio_title = \
 
854
            " Blacklisted packages with a higher version than in 'Lucid'"
 
855
        radio_option_matches = soupmatchers.HTMLContains(
 
856
            soupmatchers.Tag(
 
857
                "radio displays parent's name", 'label',
 
858
                text=radio_title),
 
859
            )
 
860
        self.assertThat(view.render(), radio_option_matches)
 
861
 
 
862
    def _set_source_selection(self, series):
 
863
        # Set up source package format selection so that copying will
 
864
        # work with the default dsc_format used in
 
865
        # makeSourcePackageRelease.
 
866
        getUtility(ISourcePackageFormatSelectionSet).add(
 
867
            series, SourcePackageFormat.FORMAT_1_0)
 
868
 
 
869
    def test_batch_filtered(self):
 
870
        # The name_filter parameter allows filtering of packages by name.
 
871
        set_derived_series_ui_feature_flag(self)
 
872
        derived_series = self.factory.makeDistroSeries(
 
873
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
874
                name='lucid'))
 
875
        diff1 = self.factory.makeDistroSeriesDifference(
 
876
            derived_series=derived_series,
 
877
            source_package_name_str="my-src-package")
 
878
        diff2 = self.factory.makeDistroSeriesDifference(
 
879
            derived_series=derived_series,
 
880
            source_package_name_str="my-second-src-package")
 
881
 
 
882
        filtered_view = create_initialized_view(
 
883
            derived_series,
 
884
            '+localpackagediffs',
 
885
            query_string='field.name_filter=my-src-package')
 
886
        unfiltered_view = create_initialized_view(
 
887
            derived_series,
 
888
            '+localpackagediffs')
 
889
 
 
890
        self.assertContentEqual(
 
891
            [diff1], filtered_view.cached_differences.batch)
 
892
        self.assertContentEqual(
 
893
            [diff2, diff1], unfiltered_view.cached_differences.batch)
 
894
 
 
895
    def test_batch_non_blacklisted(self):
 
896
        # The default filter is all non blacklisted differences.
 
897
        set_derived_series_ui_feature_flag(self)
 
898
        derived_series = self.factory.makeDistroSeries(
 
899
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
900
                name='lucid'))
 
901
        diff1 = self.factory.makeDistroSeriesDifference(
 
902
            derived_series=derived_series,
 
903
            source_package_name_str="my-src-package")
 
904
        diff2 = self.factory.makeDistroSeriesDifference(
 
905
            derived_series=derived_series,
 
906
            source_package_name_str="my-second-src-package")
 
907
        self.factory.makeDistroSeriesDifference(
 
908
            derived_series=derived_series,
 
909
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
 
910
 
 
911
        filtered_view = create_initialized_view(
 
912
            derived_series,
 
913
            '+localpackagediffs',
 
914
            query_string='field.package_type=%s' % NON_BLACKLISTED)
 
915
        filtered_view2 = create_initialized_view(
 
916
            derived_series,
 
917
            '+localpackagediffs')
 
918
 
 
919
        self.assertContentEqual(
 
920
            [diff2, diff1], filtered_view.cached_differences.batch)
 
921
        self.assertContentEqual(
 
922
            [diff2, diff1], filtered_view2.cached_differences.batch)
 
923
 
 
924
    def test_batch_differences_packages(self):
 
925
        # field.package_type parameter allows to list only
 
926
        # blacklisted differences.
 
927
        set_derived_series_ui_feature_flag(self)
 
928
        derived_series = self.factory.makeDistroSeries(
 
929
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
930
                name='lucid'))
 
931
        blacklisted_diff = self.factory.makeDistroSeriesDifference(
 
932
            derived_series=derived_series,
 
933
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
 
934
 
 
935
        blacklisted_view = create_initialized_view(
 
936
            derived_series,
 
937
            '+localpackagediffs',
 
938
            query_string='field.package_type=%s' % BLACKLISTED)
 
939
        unblacklisted_view = create_initialized_view(
 
940
            derived_series,
 
941
            '+localpackagediffs')
 
942
 
 
943
        self.assertContentEqual(
 
944
            [blacklisted_diff], blacklisted_view.cached_differences.batch)
 
945
        self.assertContentEqual(
 
946
            [], unblacklisted_view.cached_differences.batch)
 
947
 
 
948
    def test_batch_blacklisted_differences_with_higher_version(self):
 
949
        # field.package_type parameter allows to list only
 
950
        # blacklisted differences with a child's version higher than parent's.
 
951
        set_derived_series_ui_feature_flag(self)
 
952
        derived_series = self.factory.makeDistroSeries(
 
953
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
954
                name='lucid'))
 
955
        blacklisted_diff_higher = self.factory.makeDistroSeriesDifference(
 
956
            derived_series=derived_series,
 
957
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
 
958
            versions={'base': '1.1', 'parent': '1.3', 'derived': '1.10'})
 
959
        self.factory.makeDistroSeriesDifference(
 
960
            derived_series=derived_series,
 
961
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
 
962
            versions={'base': '1.1', 'parent': '1.12', 'derived': '1.10'})
 
963
 
 
964
        blacklisted_view = create_initialized_view(
 
965
            derived_series,
 
966
            '+localpackagediffs',
 
967
            query_string='field.package_type=%s' % HIGHER_VERSION_THAN_PARENT)
 
968
        unblacklisted_view = create_initialized_view(
 
969
            derived_series,
 
970
            '+localpackagediffs')
 
971
 
 
972
        self.assertContentEqual(
 
973
            [blacklisted_diff_higher],
 
974
            blacklisted_view.cached_differences.batch)
 
975
        self.assertContentEqual(
 
976
            [], unblacklisted_view.cached_differences.batch)
 
977
 
 
978
    def test_batch_resolved_differences(self):
 
979
        # Test that we can search for differences that we marked
 
980
        # resolved.
 
981
        set_derived_series_ui_feature_flag(self)
 
982
        derived_series = self.factory.makeDistroSeries(
 
983
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
984
                name='lucid'))
 
985
 
 
986
        self.factory.makeDistroSeriesDifference(
 
987
            derived_series=derived_series,
 
988
            source_package_name_str="my-src-package")
 
989
        self.factory.makeDistroSeriesDifference(
 
990
            derived_series=derived_series,
 
991
            source_package_name_str="my-second-src-package")
 
992
        resolved_diff = self.factory.makeDistroSeriesDifference(
 
993
            derived_series=derived_series,
 
994
            status=DistroSeriesDifferenceStatus.RESOLVED)
 
995
 
 
996
        filtered_view = create_initialized_view(
 
997
            derived_series,
 
998
            '+localpackagediffs',
 
999
            query_string='field.package_type=%s' % RESOLVED)
 
1000
 
 
1001
        self.assertContentEqual(
 
1002
            [resolved_diff], filtered_view.cached_differences.batch)
 
1003
 
 
1004
    def test_canPerformSync_non_editor(self):
 
1005
        # Non-editors do not see options to sync.
 
1006
        derived_series = self.factory.makeDistroSeries(
 
1007
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
1008
                name='lucid'))
 
1009
        self.factory.makeDistroSeriesDifference(
 
1010
            derived_series=derived_series)
 
1011
 
 
1012
        set_derived_series_ui_feature_flag(self)
 
1013
        with person_logged_in(self.factory.makePerson()):
 
1014
            view = create_initialized_view(
 
1015
                derived_series, '+localpackagediffs')
 
1016
 
 
1017
        self.assertFalse(view.canPerformSync())
 
1018
 
 
1019
    def test_canPerformSync_editor(self):
 
1020
        # Editors are presented with options to perform syncs.
 
1021
        derived_series = self.factory.makeDistroSeries(
 
1022
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
1023
                name='lucid'))
 
1024
        self.factory.makeDistroSeriesDifference(
 
1025
            derived_series=derived_series)
 
1026
 
 
1027
        set_derived_series_ui_feature_flag(self)
 
1028
        with person_logged_in(derived_series.owner):
 
1029
            view = create_initialized_view(
 
1030
                derived_series, '+localpackagediffs')
 
1031
            self.assertTrue(view.canPerformSync())
 
1032
 
 
1033
    def test_sync_notification_on_success(self):
 
1034
        # Syncing one or more diffs results in a stub notification.
 
1035
        versions = {
 
1036
            'base': '1.0',
 
1037
            'derived': '1.0derived1',
 
1038
            'parent': '1.0-1',
 
1039
        }
 
1040
        parent_series = self.factory.makeDistroSeries(name='warty')
 
1041
        derived_distro = self.factory.makeDistribution(name='deribuntu')
 
1042
        derived_series = self.factory.makeDistroSeries(
 
1043
            distribution=derived_distro, name='derilucid',
 
1044
            parent_series=parent_series)
 
1045
        self._set_source_selection(derived_series)
 
1046
        difference = self.factory.makeDistroSeriesDifference(
 
1047
            source_package_name_str='my-src-name',
 
1048
            derived_series=derived_series, versions=versions)
 
1049
 
 
1050
        # The inital state is that 1.0-1 is not in the derived series.
 
1051
        pubs = derived_series.main_archive.getPublishedSources(
 
1052
            name='my-src-name', version=versions['parent'],
 
1053
            distroseries=derived_series).any()
 
1054
        self.assertIs(None, pubs)
 
1055
 
 
1056
        # Now, sync the source from the parent using the form.
 
1057
        set_derived_series_ui_feature_flag(self)
 
1058
        with person_logged_in(derived_series.owner):
 
1059
            view = create_initialized_view(
 
1060
                derived_series, '+localpackagediffs',
 
1061
                method='POST', form={
 
1062
                    'field.selected_differences': [
 
1063
                        difference.source_package_name.name,
 
1064
                        ],
 
1065
                    'field.actions.sync': 'Sync',
 
1066
                    })
 
1067
 
 
1068
        # The parent's version should now be in the derived series:
 
1069
        pub = derived_series.main_archive.getPublishedSources(
 
1070
            name='my-src-name', version=versions['parent'],
 
1071
            distroseries=derived_series).one()
 
1072
        self.assertIsNot(None, pub)
 
1073
        self.assertEqual(versions['parent'], pub.sourcepackagerelease.version)
 
1074
 
 
1075
        # The view should show no errors, and the notification should
 
1076
        # confirm the sync worked.
 
1077
        self.assertEqual(0, len(view.errors))
 
1078
        notifications = view.request.response.notifications
 
1079
        self.assertEqual(1, len(notifications))
 
1080
        self.assertEqual(
 
1081
            '<p>Packages copied to '
 
1082
            '<a href="http://launchpad.dev/deribuntu/derilucid"'
 
1083
            '>Derilucid</a>:</p>\n<ul>\n<li>my-src-name 1.0-1 in '
 
1084
            'derilucid</li>\n</ul>',
 
1085
            notifications[0].message)
 
1086
        # 302 is a redirect back to the same page.
 
1087
        self.assertEqual(302, view.request.response.getStatus())
 
1088
 
 
1089
    def test_sync_error_nothing_selected(self):
 
1090
        # An error is raised when a sync is requested without any selection.
 
1091
        derived_series = self.factory.makeDistroSeries(
 
1092
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
1093
                name='lucid'))
 
1094
        self.factory.makeDistroSeriesDifference(
 
1095
            source_package_name_str='my-src-name',
 
1096
            derived_series=derived_series)
 
1097
 
 
1098
        set_derived_series_ui_feature_flag(self)
 
1099
        with person_logged_in(derived_series.owner):
 
1100
            view = create_initialized_view(
 
1101
                derived_series, '+localpackagediffs',
 
1102
                method='POST', form={
 
1103
                    'field.selected_differences': [],
 
1104
                    'field.actions.sync': 'Sync',
 
1105
                    })
 
1106
 
 
1107
        self.assertEqual(1, len(view.errors))
 
1108
        self.assertEqual(
 
1109
            'No differences selected.', view.errors[0])
 
1110
 
 
1111
    def test_sync_error_invalid_selection(self):
 
1112
        # An error is raised when an invalid difference is selected.
 
1113
        derived_series = self.factory.makeDistroSeries(
 
1114
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
1115
                name='lucid'))
 
1116
        self._set_source_selection(derived_series)
 
1117
        self.factory.makeDistroSeriesDifference(
 
1118
            source_package_name_str='my-src-name',
 
1119
            derived_series=derived_series)
 
1120
 
 
1121
        set_derived_series_ui_feature_flag(self)
 
1122
        with person_logged_in(derived_series.owner):
 
1123
            view = create_initialized_view(
 
1124
                derived_series, '+localpackagediffs',
 
1125
                method='POST', form={
 
1126
                    'field.selected_differences': ['some-other-name'],
 
1127
                    'field.actions.sync': 'Sync',
 
1128
                    })
 
1129
 
 
1130
        self.assertEqual(2, len(view.errors))
 
1131
        self.assertEqual(
 
1132
            'No differences selected.', view.errors[0])
 
1133
        self.assertEqual(
 
1134
            'Invalid value', view.errors[1].error_name)
 
1135
 
 
1136
 
 
1137
class TestDistroSeriesNeedsPackagesView(TestCaseWithFactory):
 
1138
    """Test the distroseries +needs-packaging view."""
 
1139
 
 
1140
    layer = LaunchpadZopelessLayer
 
1141
 
 
1142
    def test_cached_unlinked_packages(self):
 
1143
        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
 
1144
        distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
 
1145
        view = create_initialized_view(distroseries, '+needs-packaging')
 
1146
        self.assertTrue(
 
1147
            IResultSet.providedBy(
 
1148
                view.cached_unlinked_packages.currentBatch().list),
 
1149
            '%s should batch IResultSet so that slicing will limit the '
 
1150
            'query' % view.cached_unlinked_packages.currentBatch().list)
 
1151
 
 
1152
 
 
1153
class DistroSeriesMissingPackageDiffsTestCase(TestCaseWithFactory):
 
1154
    """Test the distroseries +missingpackages view."""
 
1155
 
 
1156
    layer = LaunchpadZopelessLayer
 
1157
 
 
1158
    def test_missingpackages_differences(self):
 
1159
        # The view fetches the differences with type
 
1160
        # MISSING_FROM_DERIVED_SERIES.
 
1161
        derived_series = self.factory.makeDistroSeries(
 
1162
            parent_series=self.factory.makeDistroSeries())
 
1163
 
 
1164
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
 
1165
        # Missing blacklisted diff.
 
1166
        self.factory.makeDistroSeriesDifference(
 
1167
            difference_type=missing_type,
 
1168
            derived_series=derived_series,
 
1169
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
 
1170
 
 
1171
        missing_diff = self.factory.makeDistroSeriesDifference(
 
1172
            difference_type=missing_type,
 
1173
            derived_series=derived_series,
 
1174
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
 
1175
 
 
1176
        view = create_initialized_view(
 
1177
            derived_series, '+missingpackages')
 
1178
 
 
1179
        self.assertContentEqual(
 
1180
            [missing_diff], view.cached_differences.batch)
 
1181
 
 
1182
    def test_missingpackages_differences_empty(self):
 
1183
        # The view is empty if there is no differences with type
 
1184
        # MISSING_FROM_DERIVED_SERIES.
 
1185
        derived_series = self.factory.makeDistroSeries(
 
1186
            parent_series=self.factory.makeDistroSeries())
 
1187
 
 
1188
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
 
1189
 
 
1190
        # Missing diff.
 
1191
        self.factory.makeDistroSeriesDifference(
 
1192
            difference_type=not_missing_type,
 
1193
            derived_series=derived_series,
 
1194
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
 
1195
 
 
1196
        view = create_initialized_view(
 
1197
            derived_series, '+missingpackages')
 
1198
 
 
1199
        self.assertContentEqual(
 
1200
            [], view.cached_differences.batch)
 
1201
 
 
1202
 
 
1203
class DistroSeriesMissingPackagesPageTestCase(DistroSeriesDifferenceMixin,
 
1204
                                              TestCaseWithFactory):
 
1205
    """Test the distroseries +missingpackages page."""
 
1206
 
 
1207
    layer = DatabaseFunctionalLayer
 
1208
 
 
1209
    def setUp(self):
 
1210
        super(DistroSeriesMissingPackagesPageTestCase,
 
1211
              self).setUp('foo.bar@canonical.com')
 
1212
        set_derived_series_ui_feature_flag(self)
 
1213
        self.simple_user = self.factory.makePerson()
 
1214
 
 
1215
    def test_parent_packagesets_missingpackages(self):
 
1216
        # +missingpackages displays the packagesets in the parent.
 
1217
        missing_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
 
1218
        self.ds_diff = self.factory.makeDistroSeriesDifference(
 
1219
            difference_type=missing_type)
 
1220
 
 
1221
        with celebrity_logged_in('admin'):
 
1222
            ps = self.factory.makePackageset(
 
1223
                packages=[self.ds_diff.source_package_name],
 
1224
                distroseries=self.ds_diff.derived_series.parent_series)
 
1225
 
 
1226
        with person_logged_in(self.simple_user):
 
1227
            view = create_initialized_view(
 
1228
                self.ds_diff.derived_series,
 
1229
                '+missingpackages',
 
1230
                principal=self.simple_user)
 
1231
            html = view()
 
1232
 
 
1233
        packageset_text = re.compile('\s*' + ps.name)
 
1234
        self._test_packagesets(
 
1235
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
 
1236
 
 
1237
 
 
1238
class DistroSerieUniquePackageDiffsTestCase(TestCaseWithFactory):
 
1239
    """Test the distroseries +uniquepackages view."""
 
1240
 
 
1241
    layer = LaunchpadZopelessLayer
 
1242
 
 
1243
    def test_uniquepackages_differences(self):
 
1244
        # The view fetches the differences with type
 
1245
        # UNIQUE_TO_DERIVED_SERIES.
 
1246
        derived_series = self.factory.makeDistroSeries(
 
1247
            name='derilucid', parent_series=self.factory.makeDistroSeries(
 
1248
                name='lucid'))
 
1249
 
 
1250
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
 
1251
        # Missing blacklisted diff.
 
1252
        self.factory.makeDistroSeriesDifference(
 
1253
            difference_type=missing_type,
 
1254
            derived_series=derived_series,
 
1255
            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
 
1256
 
 
1257
        missing_diff = self.factory.makeDistroSeriesDifference(
 
1258
            difference_type=missing_type,
 
1259
            derived_series=derived_series,
 
1260
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
 
1261
 
 
1262
        view = create_initialized_view(
 
1263
            derived_series, '+uniquepackages')
 
1264
 
 
1265
        self.assertContentEqual(
 
1266
            [missing_diff], view.cached_differences.batch)
 
1267
 
 
1268
    def test_uniquepackages_differences_empty(self):
 
1269
        # The view is empty if there is no differences with type
 
1270
        # UNIQUE_TO_DERIVED_SERIES.
 
1271
        derived_series = self.factory.makeDistroSeries(
 
1272
            parent_series=self.factory.makeDistroSeries())
 
1273
 
 
1274
        not_missing_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
 
1275
 
 
1276
        # Missing diff.
 
1277
        self.factory.makeDistroSeriesDifference(
 
1278
            difference_type=not_missing_type,
 
1279
            derived_series=derived_series,
 
1280
            status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION)
 
1281
 
 
1282
        view = create_initialized_view(
 
1283
            derived_series, '+uniquepackages')
 
1284
 
 
1285
        self.assertContentEqual(
 
1286
            [], view.cached_differences.batch)
 
1287
 
 
1288
 
 
1289
class DistroSeriesUniquePackagesPageTestCase(DistroSeriesDifferenceMixin,
 
1290
                                             TestCaseWithFactory):
 
1291
    """Test the distroseries +uniquepackages page."""
 
1292
 
 
1293
    layer = DatabaseFunctionalLayer
 
1294
 
 
1295
    def setUp(self):
 
1296
        super(DistroSeriesUniquePackagesPageTestCase,
 
1297
              self).setUp('foo.bar@canonical.com')
 
1298
        set_derived_series_ui_feature_flag(self)
 
1299
        self.simple_user = self.factory.makePerson()
 
1300
 
 
1301
    def test_packagesets_uniquepackages(self):
 
1302
        # +uniquepackages displays the packagesets in the parent.
 
1303
        missing_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
 
1304
        self.ds_diff = self.factory.makeDistroSeriesDifference(
 
1305
            difference_type=missing_type)
 
1306
 
 
1307
        with celebrity_logged_in('admin'):
 
1308
            ps = self.factory.makePackageset(
 
1309
                packages=[self.ds_diff.source_package_name],
 
1310
                distroseries=self.ds_diff.derived_series)
 
1311
 
 
1312
        with person_logged_in(self.simple_user):
 
1313
            view = create_initialized_view(
 
1314
                self.ds_diff.derived_series,
 
1315
                '+uniquepackages',
 
1316
                principal=self.simple_user)
 
1317
            html = view()
 
1318
 
 
1319
        packageset_text = re.compile('\s*' + ps.name)
 
1320
        self._test_packagesets(
 
1321
            html, packageset_text, 'packagesets', 'Packagesets')