~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

[r=adeuring][bug=751321] Reduce the per-DistroSeriesDifference-row
        database statement count on +localpackagediffs from ~15 to ~2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 
6
6
__metaclass__ = type
7
7
 
 
8
import difflib
8
9
import re
 
10
from textwrap import TextWrapper
9
11
 
10
12
from BeautifulSoup import BeautifulSoup
11
13
from lxml import html
12
14
import soupmatchers
13
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
14
21
from testtools.matchers import (
15
22
    EndsWith,
 
23
    LessThan,
16
24
    Not,
17
25
    )
18
26
from zope.component import getUtility
 
27
from zope.security.proxy import removeSecurityProxy
19
28
 
20
29
from canonical.config import config
 
30
from canonical.database.sqlbase import flush_database_caches
21
31
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
22
32
from canonical.launchpad.testing.pages import find_tag_by_id
23
33
from canonical.launchpad.webapp.batching import BatchNavigator
63
73
    login_person,
64
74
    person_logged_in,
65
75
    set_feature_flag,
 
76
    StormStatementRecorder,
66
77
    TestCaseWithFactory,
67
78
    )
 
79
from lp.testing.matchers import HasQueryCount
68
80
from lp.testing.views import create_initialized_view
69
81
 
70
82
 
488
500
        self._test_packagesets(
489
501
            html, packageset_text, 'parent-packagesets', 'Parent packagesets')
490
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
 
491
613
 
492
614
class TestDistroSeriesLocalDifferencesZopeless(TestCaseWithFactory):
493
615
    """Test the distroseries +localpackagediffs view."""
688
810
        # Delete the publications.
689
811
        difference.source_pub.status = PackagePublishingStatus.DELETED
690
812
        difference.parent_source_pub.status = PackagePublishingStatus.DELETED
 
813
        # Flush out the changes and invalidate caches (esp. property caches).
 
814
        flush_database_caches()
691
815
 
692
816
        set_derived_series_ui_feature_flag(self)
693
817
        view = create_initialized_view(