Merge lp:~bigkevmcd/offspring/non-scheduled-builds into lp:offspring
- non-scheduled-builds
- Merge into trunk
Proposed by
Kevin McDermott
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~bigkevmcd/offspring/non-scheduled-builds | ||||
Merge into: | lp:offspring | ||||
Diff against target: |
583 lines (+479/-14) 7 files modified
.bzrignore (+1/-0) lib/offspring/web/queuemanager/management/commands/build_metrics.py (+15/-0) lib/offspring/web/queuemanager/management/commands/tests/test_build_metrics.py (+28/-0) lib/offspring/web/queuemanager/metrics.py (+109/-0) lib/offspring/web/queuemanager/models.py (+7/-11) lib/offspring/web/queuemanager/tests/factory.py (+3/-3) lib/offspring/web/queuemanager/tests/test_metrics.py (+316/-0) |
||||
To merge this branch: | bzr merge lp:~bigkevmcd/offspring/non-scheduled-builds | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Offspring Committers | Pending | ||
Offspring Committers | Pending | ||
Review via email: mp+85618@code.launchpad.net |
This proposal has been superseded by a proposal from 2011-12-14.
Commit message
Description of the change
This adds metrics for requested builds.
To post a comment you must log in.
- 128. By Kevin McDermott
-
Merge forward.
- 129. By Kevin McDermott
-
Merge forward.
- 130. By Kevin McDermott
-
Merge forward.
- 131. By Kevin McDermott
-
Merge forward.
- 132. By Kevin McDermott
-
Merge forward.
- 133. By Kevin McDermott
-
Merge forward.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2011-11-18 14:33:37 +0000 |
3 | +++ .bzrignore 2011-12-14 09:29:35 +0000 |
4 | @@ -7,3 +7,4 @@ |
5 | ./config/launchpad.oauth |
6 | ./builds/ |
7 | ./scripts/ |
8 | +.coverage |
9 | |
10 | === added directory 'lib/offspring/web/queuemanager/management' |
11 | === added file 'lib/offspring/web/queuemanager/management/__init__.py' |
12 | === added directory 'lib/offspring/web/queuemanager/management/commands' |
13 | === added file 'lib/offspring/web/queuemanager/management/commands/__init__.py' |
14 | === added file 'lib/offspring/web/queuemanager/management/commands/build_metrics.py' |
15 | --- lib/offspring/web/queuemanager/management/commands/build_metrics.py 1970-01-01 00:00:00 +0000 |
16 | +++ lib/offspring/web/queuemanager/management/commands/build_metrics.py 2011-12-14 09:29:35 +0000 |
17 | @@ -0,0 +1,15 @@ |
18 | +# Copyright 2010-2011 Canonical Ltd. |
19 | + |
20 | +from django.core.management.base import NoArgsCommand |
21 | + |
22 | +from offspring.web.queuemanager.metrics import get_build_result_metrics |
23 | + |
24 | + |
25 | +class Command(NoArgsCommand): |
26 | + help = "Outputs some basic metrics." |
27 | + |
28 | + def handle_noargs(self, **options): |
29 | + metrics = get_build_result_metrics() |
30 | + |
31 | + for item, value in metrics: |
32 | + self.stdout.write("%s = %s\n" % (item, value)) |
33 | |
34 | === added directory 'lib/offspring/web/queuemanager/management/commands/tests' |
35 | === added file 'lib/offspring/web/queuemanager/management/commands/tests/__init__.py' |
36 | === added file 'lib/offspring/web/queuemanager/management/commands/tests/test_build_metrics.py' |
37 | --- lib/offspring/web/queuemanager/management/commands/tests/test_build_metrics.py 1970-01-01 00:00:00 +0000 |
38 | +++ lib/offspring/web/queuemanager/management/commands/tests/test_build_metrics.py 2011-12-14 09:29:35 +0000 |
39 | @@ -0,0 +1,28 @@ |
40 | +# Copyright 2010-2011 Canonical Ltd. |
41 | +from cStringIO import StringIO |
42 | + |
43 | +from mocker import MockerTestCase |
44 | + |
45 | +from offspring.web.queuemanager.metrics import get_build_result_metrics |
46 | +from offspring.web.queuemanager.management.commands.build_metrics import ( |
47 | + Command) |
48 | + |
49 | + |
50 | +class TestBuildResultMetricsCommand(MockerTestCase): |
51 | + |
52 | + def test_calls_get_build_result_metrics(self): |
53 | + """ |
54 | + When called, the build_metrics command should fetch the list of metrics |
55 | + available for offspring.web.queuemanager and output them. |
56 | + """ |
57 | + build_result_metrics_mock = self.mocker.replace(get_build_result_metrics) |
58 | + build_result_metrics_mock() |
59 | + self.mocker.result([("Description of value", 200)]) |
60 | + self.mocker.replay() |
61 | + |
62 | + command = Command() |
63 | + stdout = StringIO() |
64 | + command.stdout = stdout |
65 | + command.handle_noargs() |
66 | + |
67 | + self.assertEqual("Description of value = 200\n", stdout.getvalue()) |
68 | |
69 | === added file 'lib/offspring/web/queuemanager/metrics.py' |
70 | --- lib/offspring/web/queuemanager/metrics.py 1970-01-01 00:00:00 +0000 |
71 | +++ lib/offspring/web/queuemanager/metrics.py 2011-12-14 09:29:35 +0000 |
72 | @@ -0,0 +1,109 @@ |
73 | +from datetime import datetime, timedelta |
74 | + |
75 | +from offspring.web.queuemanager.models import BuildResult |
76 | +from offspring.enums import ProjectBuildStates |
77 | + |
78 | + |
79 | +def get_average_time(query_params, columns, project=None, automated=None): |
80 | + |
81 | + """ |
82 | + Return the average time between the two named columns. |
83 | + |
84 | + The columns must be listed as the start_time and finish_time for the |
85 | + metric. |
86 | + |
87 | + If no Project is supplied, average the times across all projects. |
88 | + |
89 | + query_params: A dictionary of Djanqo query filters. |
90 | + columns: Columns to be queried, must be start_time, end_time. |
91 | + |
92 | + project: A Project to filter BuildResults with, if none supplied, will use |
93 | + all Projects |
94 | + automated: If not None, filter BuildRequests using the requestor, if True, |
95 | + only BuildRequests with no requestor will be used, otherwise |
96 | + BuildRequests with the requestor field populated will be used. |
97 | + """ |
98 | + # FIXME: This should really be done using PostgreSQL queries, but the tests |
99 | + # don't run using PostgreSQL... |
100 | + def delta_to_minutes(delta): |
101 | + """ |
102 | + Return the total amount of time represented by a timedelta. |
103 | + """ |
104 | + return ((delta.microseconds + (delta.seconds + delta.days |
105 | + * 24 * 3600) |
106 | + * 10 ** 6) / 10 ** 6) |
107 | + if project is not None: |
108 | + query_params["project"] = project |
109 | + |
110 | + if automated is not None: |
111 | + query_params["requestor__isnull"] = automated |
112 | + build_results = BuildResult.objects.filter(**query_params) |
113 | + time_stamps = build_results.values_list(*columns) |
114 | + if not time_stamps: |
115 | + return 0.0 |
116 | + times = [delta_to_minutes(end_time - start_time) |
117 | + for (start_time, end_time) in time_stamps] |
118 | + return float(sum(times)) / len(time_stamps) |
119 | + |
120 | + |
121 | +def get_average_build_time(project=None, automated=None, period=None): |
122 | + """ |
123 | + Return the average time taken between BuildResult.started_at and |
124 | + BuildResult.finished_at in seconds for all BuildResults associated with the |
125 | + provided Project. |
126 | + |
127 | + period: A tuple of datetime objects (start_time, end_time) |
128 | + |
129 | + See get_average_time for the project and automated parameters. |
130 | + """ |
131 | + query_params = {"finished_at__isnull": False, |
132 | + "result__exact": ProjectBuildStates.SUCCESS} |
133 | + if period is not None: |
134 | + query_params["finished_at__range"] = period |
135 | + return get_average_time(query_params, ("started_at", "finished_at"), |
136 | + project=project, automated=automated) |
137 | + |
138 | + |
139 | +def get_average_queue_time(project=None, automated=None, period=None): |
140 | + """ |
141 | + Return the average time taken between BuildResult.requested_at and |
142 | + BuildResult.dispatched_at in seconds for all BuildResults associated with the |
143 | + provided Project. |
144 | + |
145 | + period: A tuple of datetime objects (start_time, end_time) |
146 | + |
147 | + See get_average_time for the project and automated parameters. |
148 | + """ |
149 | + query_params = {} |
150 | + if period is not None: |
151 | + query_params["dispatched_at__range"] = period |
152 | + return get_average_time(query_params, ("requested_at", "dispatched_at"), |
153 | + project=project, automated=automated) |
154 | + |
155 | + |
156 | +def get_build_result_metrics(): |
157 | + """ |
158 | + Return some simple metrics, in a format suitable for iterating over and |
159 | + displaying in a command-line tool. |
160 | + """ |
161 | + results = [] |
162 | + start_date = datetime.utcnow() |
163 | + |
164 | + seven_days = (start_date - timedelta(days=7), start_date) |
165 | + thirty_days = (start_date - timedelta(days=30), start_date) |
166 | + ninety_days = (start_date - timedelta(days=90), start_date) |
167 | + |
168 | + build_types = [("scheduled", True), ("requested", False)] |
169 | + build_periods = [("7 days", seven_days), ("30 days", thirty_days), |
170 | + ("90 days", ninety_days)] |
171 | + |
172 | + for build_type, automated in build_types: |
173 | + for function, metric_type in [(get_average_build_time, "build"), |
174 | + (get_average_queue_time, "queue")]: |
175 | + for text, period in build_periods: |
176 | + metric = "Average %s %s time (%s)" % (build_type, metric_type, |
177 | + text) |
178 | + results.append((metric, |
179 | + function(period=period, automated=automated))) |
180 | + |
181 | + return results |
182 | |
183 | === modified file 'lib/offspring/web/queuemanager/models.py' |
184 | --- lib/offspring/web/queuemanager/models.py 2011-12-08 16:27:50 +0000 |
185 | +++ lib/offspring/web/queuemanager/models.py 2011-12-14 09:29:35 +0000 |
186 | @@ -1,15 +1,7 @@ |
187 | # Copyright 2010 Canonical Ltd. This software is licensed under the |
188 | # GNU Affero General Public License version 3 (see the file LICENSE). |
189 | |
190 | -__all__ = ["Project", "ProjectGroup", "Lexbuilder", "BuildRequest", \ |
191 | - "BuildResult", "DailyBuildOrder", "LaunchpadProject", "LaunchpadProjectMilestone", \ |
192 | - "ProjectNotificationSubscription", 'Release'] |
193 | - |
194 | -from datetime import ( |
195 | - date, |
196 | - datetime, |
197 | - timedelta |
198 | -) |
199 | +from datetime import date, datetime, timedelta |
200 | import math |
201 | |
202 | from django.conf import settings |
203 | @@ -25,6 +17,10 @@ |
204 | AccessManager) |
205 | from offspring.enums import ProjectBuildStates |
206 | |
207 | +__all__ = ["Project", "ProjectGroup", "Lexbuilder", "BuildRequest", |
208 | + "BuildResult", "DailyBuildOrder", "LaunchpadProject", |
209 | + "LaunchpadProjectMilestone", "ProjectNotificationSubscription", |
210 | + "Release"] |
211 | |
212 | ARCH_CHOICES = ( |
213 | (u'i386', u'i386'), |
214 | @@ -82,7 +78,7 @@ |
215 | project_group=self) |
216 | |
217 | @property |
218 | - def builder(self): |
219 | + def builder(self): |
220 | return None |
221 | |
222 | @property |
223 | @@ -386,7 +382,7 @@ |
224 | requestor = models.ForeignKey(User, db_column="requestor_id", blank=True, null=True, editable=False) |
225 | reason = models.CharField('request reason', blank=True, null=True, max_length=200, editable=False) |
226 | requested_at = models.DateTimeField('date created', auto_now_add=True, blank=True, null=True, editable=False) |
227 | - dispatched_at = models.DateTimeField('date dispatched', editable=False, null=True, blank=True) |
228 | + dispatched_at = models.DateTimeField('date dispatched', editable=False, null=True, blank=True) |
229 | notes = models.CharField(max_length=200, null=True, blank=True) |
230 | |
231 | access_relation = 'project' |
232 | |
233 | === modified file 'lib/offspring/web/queuemanager/tests/factory.py' |
234 | --- lib/offspring/web/queuemanager/tests/factory.py 2011-12-02 13:55:56 +0000 |
235 | +++ lib/offspring/web/queuemanager/tests/factory.py 2011-12-14 09:29:35 +0000 |
236 | @@ -59,7 +59,6 @@ |
237 | project_group=project_group) |
238 | for group in access_groups: |
239 | project.access_groups.add(group) |
240 | - project.save() |
241 | return project |
242 | |
243 | def make_project_group(self, name=None, title=None): |
244 | @@ -73,7 +72,8 @@ |
245 | title = self.get_unique_string() |
246 | return ProjectGroup.objects.create(name=name, title=title) |
247 | |
248 | - def make_build_result(self, project=None, name=None, result=None): |
249 | + def make_build_result(self, project=None, name=None, result=None, |
250 | + requestor=None): |
251 | """ |
252 | Create a BuildResult with the given project or a newly created one if |
253 | none is given. |
254 | @@ -83,7 +83,7 @@ |
255 | if project is None: |
256 | project = self.make_project() |
257 | return BuildResult.objects.create( |
258 | - project=project, name=name, result=result) |
259 | + project=project, name=name, result=result, requestor=requestor) |
260 | |
261 | def make_release(self, build=None, name=None, creator=None): |
262 | """ |
263 | |
264 | === added file 'lib/offspring/web/queuemanager/tests/test_metrics.py' |
265 | --- lib/offspring/web/queuemanager/tests/test_metrics.py 1970-01-01 00:00:00 +0000 |
266 | +++ lib/offspring/web/queuemanager/tests/test_metrics.py 2011-12-14 09:29:35 +0000 |
267 | @@ -0,0 +1,316 @@ |
268 | +from datetime import datetime, timedelta, date, time |
269 | + |
270 | +from django.test import TestCase |
271 | + |
272 | +from offspring.enums import ProjectBuildStates |
273 | +from offspring.web.queuemanager.models import User |
274 | +from offspring.web.queuemanager.metrics import ( |
275 | + get_average_build_time, get_average_queue_time, |
276 | + get_build_result_metrics) |
277 | + |
278 | +from offspring.web.queuemanager.tests.factory import factory |
279 | + |
280 | + |
281 | +def create_build_results_with_durations(project, durations, |
282 | + requestor=None, |
283 | + build_date=date.today(), |
284 | + result=ProjectBuildStates.SUCCESS): |
285 | + """ |
286 | + Create BuildResults with durations specified. |
287 | + |
288 | + project: The Project to associate the BuildResults with. |
289 | + durations: A sequence of time periods in minutes. |
290 | + """ |
291 | + finished_at = datetime.combine(build_date, time()) |
292 | + for minutes in durations: |
293 | + build_result = factory.make_build_result( |
294 | + project=project, requestor=requestor, result=result) |
295 | + build_result.finished_at = finished_at |
296 | + build_result.started_at = finished_at - timedelta(minutes=minutes) |
297 | + build_result.save() |
298 | + |
299 | + |
300 | +def create_build_results_with_queue_times(project, queue_times, requestor=None, |
301 | + build_date=date.today()): |
302 | + """ |
303 | + Create BuildResults with queue times specified. |
304 | + |
305 | + project: The Project to associate the BuildResults with. |
306 | + queue_times: A sequence of time periods in minutes. |
307 | + """ |
308 | + dispatched_at = datetime.combine(build_date, time()) |
309 | + for minutes in queue_times: |
310 | + build_result = factory.make_build_result( |
311 | + project=project, requestor=requestor) |
312 | + build_result.dispatched_at = dispatched_at |
313 | + build_result.requested_at = dispatched_at - timedelta(minutes=minutes) |
314 | + build_result.save() |
315 | + |
316 | + |
317 | +class BuildResultMetricsTests(TestCase): |
318 | + |
319 | + def setUp(self): |
320 | + self.project = factory.make_project() |
321 | + |
322 | + def test_get_average_build_time_single_build(self): |
323 | + """ |
324 | + get_average_build_time should return the average time taken to build a |
325 | + project. |
326 | + """ |
327 | + build_result = factory.make_build_result( |
328 | + project=self.project, result=ProjectBuildStates.SUCCESS) |
329 | + finished_at = datetime.now() |
330 | + build_result.finished_at = finished_at |
331 | + build_result.started_at = finished_at - timedelta(minutes=10) |
332 | + build_result.save() |
333 | + self.assertEqual(10*60, get_average_build_time(self.project)) |
334 | + |
335 | + def test_get_average_build_time_only_includes_completed_builds(self): |
336 | + """ |
337 | + get_average_build_time should only include builds that are in a |
338 | + completed state. |
339 | + """ |
340 | + create_build_results_with_durations( |
341 | + self.project, [10, 20], result=ProjectBuildStates.UNKNOWN) |
342 | + create_build_results_with_durations( |
343 | + self.project, [10, 20], result=ProjectBuildStates.FAILED) |
344 | + create_build_results_with_durations( |
345 | + self.project, [10, 20], result=ProjectBuildStates.PENDING) |
346 | + |
347 | + self.assertEqual(0.0, get_average_build_time(self.project)) |
348 | + |
349 | + def test_get_average_build_time_no_builds(self): |
350 | + """ |
351 | + If there are no builds, we should get an average of 0.0. |
352 | + """ |
353 | + self.assertEqual(0.0, get_average_build_time(self.project)) |
354 | + |
355 | + def test_get_average_build_time_unfinished_build(self): |
356 | + """ |
357 | + If a build has started, but not yet finished, this we should get 0.0 |
358 | + for the average for this entry. |
359 | + """ |
360 | + build_result = factory.make_build_result(project=self.project) |
361 | + build_result.started_at = datetime.now() - timedelta(minutes=20) |
362 | + build_result.save() |
363 | + self.assertEqual(0.0, get_average_build_time(self.project)) |
364 | + |
365 | + def test_get_average_build_time_multiple_builds(self): |
366 | + """ |
367 | + The time for each BuildResult should be averaged together. |
368 | + """ |
369 | + create_build_results_with_durations(self.project, [10, 20]) |
370 | + self.assertEqual(15*60, get_average_build_time(self.project)) |
371 | + |
372 | + def test_get_average_build_time_multiple_builds_ignores_unfinished(self): |
373 | + """ |
374 | + Any unfinished BuildResults should be discounted from the calculation. |
375 | + """ |
376 | + create_build_results_with_durations(self.project, [10, 20]) |
377 | + |
378 | + # Create an additional unfinished build. |
379 | + build_result = factory.make_build_result(project=self.project) |
380 | + build_result.started_at = datetime.now() - timedelta(minutes=20) |
381 | + build_result.save() |
382 | + |
383 | + self.assertEqual(15*60, get_average_build_time(self.project)) |
384 | + |
385 | + def test_get_average_build_time_ignores_other_projects(self): |
386 | + """ |
387 | + BuildResults associated with other projects should not be included in |
388 | + the calculation. |
389 | + """ |
390 | + create_build_results_with_durations(self.project, [10, 20]) |
391 | + create_build_results_with_durations(factory.make_project(), [50, 100]) |
392 | + self.assertEqual(15*60, get_average_build_time(self.project)) |
393 | + |
394 | + def test_get_average_build_time_no_project(self): |
395 | + """ |
396 | + If get_average_build_time is not supplied with a Project, then it |
397 | + should calculate the average across all projects. |
398 | + """ |
399 | + create_build_results_with_durations(self.project, [10, 20]) |
400 | + create_build_results_with_durations( |
401 | + factory.make_project(), [50, 100]) |
402 | + # 10 + 20 + 50 + 100 = 180 / 4 = 45 minutes... |
403 | + self.assertEqual(45*60, get_average_build_time()) |
404 | + |
405 | + def test_get_average_build_time_with_automated_flag(self): |
406 | + """ |
407 | + Calling get_average_build_time with automated=True should filter the |
408 | + results to only include automated builds, which are defined as builds |
409 | + with a requestor. |
410 | + """ |
411 | + user = User.objects.create_user(u"Testing", u"testing@example.com", |
412 | + u"testing") |
413 | + create_build_results_with_durations(self.project, [10, 20]) |
414 | + create_build_results_with_durations( |
415 | + factory.make_project(), [50, 100], requestor=user) |
416 | + |
417 | + def test_get_average_build_time_filters_by_date(self): |
418 | + """ |
419 | + It should be possible to restrict the date queried to a specific time |
420 | + frame. |
421 | + |
422 | + Any BuildResults finishing outwith the time period specified shouldn't |
423 | + be included. |
424 | + """ |
425 | + build_date = datetime.utcnow() - timedelta(days=30) |
426 | + create_build_results_with_durations( |
427 | + self.project, [10, 20], build_date=build_date) |
428 | + |
429 | + # From the day after we create our builds 'til today. |
430 | + time_period = (build_date + timedelta(days=1), datetime.utcnow()) |
431 | + self.assertEqual(0.0, |
432 | + get_average_build_time(period=time_period)) |
433 | + |
434 | + def test_get_average_build_time_includes_by_date(self): |
435 | + """ |
436 | + It should be possible to restrict the date queried to a specific time |
437 | + frame, specifying a period should only include those BuildResults |
438 | + within that period. |
439 | + """ |
440 | + build_date = datetime.utcnow() - timedelta(days=30) |
441 | + create_build_results_with_durations( |
442 | + self.project, [10, 20], build_date=build_date) |
443 | + |
444 | + create_build_results_with_durations( |
445 | + factory.make_project(), [50, 100], |
446 | + build_date=build_date + timedelta(days=2)) |
447 | + |
448 | + # From the day after we create our builds 'til today. |
449 | + time_period = (build_date + timedelta(days=1), datetime.utcnow()) |
450 | + self.assertEqual(75*60, get_average_build_time(period=time_period)) |
451 | + |
452 | + def test_get_average_build_time_filters_by_date_and_automated(self): |
453 | + """ |
454 | + It should be possible to restrict the date queried to a specific time |
455 | + frame, specifying a period and the automated flag should filter the |
456 | + applicable BuildResults. |
457 | + """ |
458 | + user = User.objects.create_user(u"Testing", u"testing@example.com", |
459 | + u"testing") |
460 | + build_date = datetime.utcnow() - timedelta(days=30) |
461 | + create_build_results_with_durations( |
462 | + self.project, [10, 20], build_date=build_date, |
463 | + requestor=user) |
464 | + create_build_results_with_durations( |
465 | + factory.make_project(), [40, 40], |
466 | + build_date=build_date + timedelta(days=2)) |
467 | + |
468 | + # From the day after we create our builds 'til today. |
469 | + time_period = (build_date + timedelta(days=1), datetime.utcnow()) |
470 | + self.assertEqual(40*60, |
471 | + get_average_build_time(period=time_period, |
472 | + automated=True)) |
473 | + |
474 | + |
475 | +class QueueTimeMetricsTests(TestCase): |
476 | + |
477 | + def setUp(self): |
478 | + self.project = factory.make_project() |
479 | + |
480 | + def test_average_queue_time_single_build(self): |
481 | + """ |
482 | + get_average_queue_time should return the average time a build waits |
483 | + before being dispatched. |
484 | + """ |
485 | + build_result = factory.make_build_result(project=self.project) |
486 | + dispatched_at = datetime.now() |
487 | + build_result.dispatched_at = dispatched_at |
488 | + build_result.requested_at = dispatched_at - timedelta(minutes=30) |
489 | + build_result.save() |
490 | + self.assertEqual(30*60, get_average_queue_time(self.project)) |
491 | + |
492 | + def test_average_queue_time_no_builds(self): |
493 | + """ |
494 | + If there are no builds, we should get an average of 0.0. |
495 | + """ |
496 | + self.assertEqual(0.0*60, get_average_queue_time(self.project)) |
497 | + |
498 | + def test_average_queue_time_includes_by_date(self): |
499 | + """ |
500 | + It should be possible to restrict the date queried to a specific time |
501 | + frame, specifying a period should only include those BuildResults |
502 | + within that period. |
503 | + """ |
504 | + build_date = datetime.utcnow() - timedelta(days=30) |
505 | + create_build_results_with_queue_times( |
506 | + self.project, [10, 20], build_date=build_date) |
507 | + |
508 | + create_build_results_with_queue_times( |
509 | + factory.make_project(), [50, 100], |
510 | + build_date=build_date + timedelta(days=2)) |
511 | + |
512 | + # From the day after we create our builds 'til today. |
513 | + time_period = (build_date + timedelta(days=1), datetime.utcnow()) |
514 | + self.assertEqual(75*60, |
515 | + get_average_queue_time(period=time_period)) |
516 | + |
517 | + |
518 | +class GetBuildResultsTests(TestCase): |
519 | + |
520 | + def setUp(self): |
521 | + self.project = factory.make_project() |
522 | + |
523 | + def test_get_build_result_metrics(self): |
524 | + """ |
525 | + get_build_result_metrics should return a list of tuples with the metric |
526 | + being measured, and the time in minutes. |
527 | + """ |
528 | + def create_test_data(user=None, offset=0): |
529 | + start_date = datetime.utcnow() |
530 | + last_week = start_date - timedelta(days=8) |
531 | + last_month = start_date - timedelta(days=35) |
532 | + # Two this week, average = 10 + 20/2 = 15 |
533 | + create_build_results_with_durations( |
534 | + self.project, [10 + offset, 20], build_date=start_date, |
535 | + requestor=user) |
536 | + |
537 | + # Two this week, average = 30 + 18/2 = 24.0 |
538 | + create_build_results_with_queue_times( |
539 | + self.project, [30 + offset, 18], build_date=start_date, |
540 | + requestor=user) |
541 | + |
542 | + # Two last week, 10 + 10 + 10 + 20/4 = 12.5 |
543 | + create_build_results_with_durations( |
544 | + self.project, [10 + offset, 10], build_date=last_week, |
545 | + requestor=user) |
546 | + |
547 | + # Two last week, average = 30 + 18 + 14 11/4 = 73 |
548 | + create_build_results_with_queue_times( |
549 | + self.project, [14+offset, 11], build_date=last_week, |
550 | + requestor=user) |
551 | + |
552 | + # Two last month, 10 + 10 + 10 + 20 + 30 + 40/6 = 20 |
553 | + create_build_results_with_durations( |
554 | + self.project, [30+offset, 40], build_date=last_month, |
555 | + requestor=user) |
556 | + |
557 | + # Two last month, average = 30 + 18 + 14 11 + 30 + 20/6 = 82 |
558 | + create_build_results_with_queue_times( |
559 | + self.project, [30+offset, 20], build_date=last_month, |
560 | + requestor=user) |
561 | + |
562 | + # Scheduled builds |
563 | + # Requested builds |
564 | + |
565 | + create_test_data() |
566 | + user = User.objects.create_user(u"Testing", u"testing@example.com", |
567 | + u"testing") |
568 | + create_test_data(user=user, offset=10) |
569 | + |
570 | + self.assertEqual( |
571 | + [("Average scheduled build time (7 days)", 15.0*60), |
572 | + ("Average scheduled build time (30 days)", 12.5*60), |
573 | + ("Average scheduled build time (90 days)", 20.0*60), |
574 | + ("Average scheduled queue time (7 days)", 24.0*60), |
575 | + ("Average scheduled queue time (30 days)", 18.25*60), |
576 | + ("Average scheduled queue time (90 days)", 20.5*60), |
577 | + ("Average requested build time (7 days)", 20.0*60), |
578 | + ("Average requested build time (30 days)", 17.5*60), |
579 | + ("Average requested build time (90 days)", 25.0*60), |
580 | + ("Average requested queue time (7 days)", 29.0*60), |
581 | + ("Average requested queue time (30 days)", 23.25*60), |
582 | + ("Average requested queue time (90 days)", 25.5*60)], |
583 | + get_build_result_metrics()) |