Merge lp:~abentley/launchpad/fix-build-time-display into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: not available
Proposed branch: lp:~abentley/launchpad/fix-build-time-display
Merge into: lp:launchpad
Diff against target: 140 lines (+69/-9)
2 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+21/-7)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+48/-2)
To merge this branch: bzr merge lp:~abentley/launchpad/fix-build-time-display
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+24263@code.launchpad.net

Commit message

No exception when checking for estimated completion time.

Description of the change

= Summary =
Fix bug #570884: build time display oopses for active builds

== Proposed fix ==
Change the displayed time so that it is always the completion time, whether
actual or estimated.

== Pre-implementation notes ==
None

== Implementation details ==
There are now two phases of ETA. When the Job is WAITING, the eta is
calculated by adding the estimated duration to the estimated start time.

When the job is not waiting, the ETA is calculated by adding the estimated
duration to the actual start time.

== Tests ==
bin/test -vt browser test_sourcepackagerecipe

== Demo and Q/A ==
Request a build. Repeatedly refresh until the build is in progress. It should
display an estimated completion time, and there should be no oops.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote :

<rockstar> abentley, I'm not sure TestSourcePackageRecipeBuildView should inherit from BrowserTestCase - TestCaseWithFactory is probably fine.
<rockstar> I say this because it doesn't actually use a browser, but just instantiates a raw view.
<abentley> rockstar, Okay.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
2--- lib/lp/code/browser/sourcepackagerecipe.py 2010-04-23 03:30:30 +0000
3+++ lib/lp/code/browser/sourcepackagerecipe.py 2010-04-28 22:49:29 +0000
4@@ -45,6 +45,7 @@
5 IArchiveSet)
6 from lp.registry.interfaces.distroseries import IDistroSeriesSet
7 from lp.registry.interfaces.pocket import PackagePublishingPocket
8+from lp.services.job.interfaces.job import JobStatus
9
10
11 class IRecipesForPerson(Interface):
12@@ -52,8 +53,7 @@
13
14
15 class RecipesForPersonBreadcrumb(Breadcrumb):
16- """A Breadcrumb that will handle the "Recipes" link for recipe breadcrumbs.
17- """
18+ """A Breadcrumb to handle the "Recipes" link for recipe breadcrumbs."""
19
20 rootsite = 'code'
21 text = 'Recipes'
22@@ -229,14 +229,26 @@
23
24 @property
25 def eta(self):
26- """The datetime when the build job is estimated to begin."""
27+ """The datetime when the build job is estimated to complete.
28+
29+ This is the BuildQueue.estimated_duration plus the
30+ Job.date_started or BuildQueue.getEstimatedJobStartTime.
31+ """
32 if self.context.buildqueue_record is None:
33 return None
34- return self.context.buildqueue_record.getEstimatedJobStartTime()
35+ queue_record = self.context.buildqueue_record
36+ if queue_record.job.status == JobStatus.WAITING:
37+ start_time = queue_record.getEstimatedJobStartTime()
38+ if start_time is None:
39+ return None
40+ else:
41+ start_time = queue_record.job.date_started
42+ duration = queue_record.estimated_duration
43+ return start_time + duration
44
45 @property
46 def date(self):
47- """The date when the build complete or will begin."""
48+ """The date when the build completed or is estimated to complete."""
49 if self.estimate:
50 return self.eta
51 return self.context.datebuilt
52@@ -244,7 +256,9 @@
53 @property
54 def estimate(self):
55 """If true, the date value is an estimate."""
56- return (self.context.datebuilt is None and self.eta is not None)
57+ if self.context.datebuilt is not None:
58+ return False
59+ return self.eta is not None
60
61
62 class ISourcePackageAddEditSchema(Interface):
63@@ -272,7 +286,7 @@
64 def validate(self, data):
65 try:
66 parser = RecipeParser(data['recipe_text'])
67- recipe_text = parser.parse()
68+ parser.parse()
69 except RecipeParseError:
70 self.setFieldError(
71 'recipe_text',
72
73=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
74--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-04-23 02:35:47 +0000
75+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-04-28 22:49:29 +0000
76@@ -19,9 +19,11 @@
77 extract_text, find_main_content, find_tags_by_class)
78 from canonical.testing import DatabaseFunctionalLayer
79 from lp.buildmaster.interfaces.buildbase import BuildStatus
80-from lp.code.browser.sourcepackagerecipe import SourcePackageRecipeView
81+from lp.code.browser.sourcepackagerecipe import (
82+ SourcePackageRecipeView, SourcePackageRecipeBuildView
83+)
84 from lp.code.interfaces.sourcepackagerecipe import MINIMAL_RECIPE_TEXT
85-from lp.testing import ANONYMOUS, BrowserTestCase, login
86+from lp.testing import ANONYMOUS, BrowserTestCase, login, TestCaseWithFactory
87
88
89 class TestCaseForRecipe(BrowserTestCase):
90@@ -330,6 +332,50 @@
91 self.assertEqual(['Secret Squirrel', 'Woody'], build_distros)
92
93
94+class TestSourcePackageRecipeBuildView(TestCaseWithFactory):
95+ """Test behaviour of SourcePackageReciptBuildView."""
96+
97+ layer = DatabaseFunctionalLayer
98+
99+ def test_estimate(self):
100+ """Time should be estimated until the job is completed."""
101+ build = self.factory.makeSourcePackageRecipeBuild()
102+ queue_entry = self.factory.makeSourcePackageRecipeBuildJob(
103+ recipe_build=build)
104+ self.factory.makeBuilder()
105+ view = SourcePackageRecipeBuildView(build, None)
106+ self.assertTrue(view.estimate)
107+ queue_entry.job.start()
108+ self.assertTrue(view.estimate)
109+ removeSecurityProxy(build).datebuilt = datetime.now(utc)
110+ self.assertFalse(view.estimate)
111+
112+ def test_eta(self):
113+ """ETA should be reasonable.
114+
115+ It should be None if there is no builder or queue entry.
116+ It should be getEstimatedJobStartTime + estimated duration for jobs
117+ that have not started.
118+ It should be job.date_started + estimated duration for jobs that have
119+ started.
120+ """
121+ build = self.factory.makeSourcePackageRecipeBuild()
122+ view = SourcePackageRecipeBuildView(build, None)
123+ self.assertIs(None, view.eta)
124+ queue_entry = self.factory.makeSourcePackageRecipeBuildJob(
125+ recipe_build=build)
126+ queue_entry._now = lambda: datetime(1970, 1, 1, 0, 0, 0, 0, utc)
127+ self.factory.makeBuilder()
128+ self.assertIsNot(None, view.eta)
129+ self.assertEqual(
130+ queue_entry.getEstimatedJobStartTime() +
131+ queue_entry.estimated_duration, view.eta)
132+ queue_entry.job.start()
133+ self.assertEqual(
134+ queue_entry.job.date_started + queue_entry.estimated_duration,
135+ view.eta)
136+
137+
138 class TestSourcePackageRecipeDeleteView(TestCaseForRecipe):
139
140 layer = DatabaseFunctionalLayer