Merge ~blake-rouse/maas:lp1723425 into maas:master

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: 6351fe88d73b42a1fb9abe2f87c7ef8cad463c19
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~blake-rouse/maas:lp1723425
Merge into: maas:master
Diff against target: 519 lines (+316/-8)
12 files modified
src/maasserver/api/scriptresults.py (+3/-0)
src/maasserver/api/tests/test_scriptresults.py (+9/-0)
src/maasserver/static/js/angular/directives/script_runtime.js (+57/-0)
src/maasserver/static/js/angular/directives/tests/test_script_runtime.js (+89/-0)
src/maasserver/static/partials/script-results-list.html (+8/-8)
src/maasserver/views/combo.py (+1/-0)
src/maasserver/websockets/handlers/node.py (+6/-0)
src/maasserver/websockets/handlers/node_result.py (+6/-0)
src/maasserver/websockets/handlers/tests/test_machine.py (+12/-0)
src/maasserver/websockets/handlers/tests/test_node_result.py (+6/-0)
src/metadataserver/models/scriptresult.py (+45/-0)
src/metadataserver/models/tests/test_scriptresult.py (+74/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Lee Trager (community) Approve
Mike Pontillo (community) Approve
Review via email: mp+333281@code.launchpad.net

Commit message

Fixes LP: #1723425 - Add a maas-script-runtime-directive to render client side the current runtime of a script.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1723425 lp:~blake-rouse/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/662/console
COMMIT: a55e2f46eeba4ca6db6548ec1ada82526f2e9186

review: Needs Fixing
Revision history for this message
Mike Pontillo (mpontillo) wrote :

New code looks good. (Of course, I trust you to fix the regressions in the test suite noted by the lander!)

review: Approve
Revision history for this message
Lee Trager (ltrager) wrote :

LGTM!

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1723425 lp:~blake-rouse/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 6351fe88d73b42a1fb9abe2f87c7ef8cad463c19

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/scriptresults.py b/src/maasserver/api/scriptresults.py
2index 80fc7f3..b11ba66 100644
3--- a/src/maasserver/api/scriptresults.py
4+++ b/src/maasserver/api/scriptresults.py
5@@ -210,6 +210,9 @@ class NodeScriptResultHandler(OperationsHandler):
6 'started': fmt_time(script_result.started),
7 'ended': fmt_time(script_result.ended),
8 'runtime': script_result.runtime,
9+ 'starttime': script_result.starttime,
10+ 'endtime': script_result.endtime,
11+ 'estimated_runtime': script_result.estimated_runtime,
12 'parameters': script_result.parameters,
13 'script_id': script_result.script_id,
14 'script_revision_id': script_result.script_version_id,
15diff --git a/src/maasserver/api/tests/test_scriptresults.py b/src/maasserver/api/tests/test_scriptresults.py
16index d80b68f..ee942a9 100644
17--- a/src/maasserver/api/tests/test_scriptresults.py
18+++ b/src/maasserver/api/tests/test_scriptresults.py
19@@ -269,6 +269,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser):
20 'started': fmt_time(script_result.started),
21 'ended': fmt_time(script_result.ended),
22 'runtime': script_result.runtime,
23+ 'starttime': script_result.starttime,
24+ 'endtime': script_result.endtime,
25+ 'estimated_runtime': script_result.estimated_runtime,
26 'parameters': script_result.parameters,
27 'script_id': script_result.script_id,
28 'script_revision_id': script_result.script_version_id,
29@@ -315,6 +318,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser):
30 'started': fmt_time(script_result.started),
31 'ended': fmt_time(script_result.ended),
32 'runtime': script_result.runtime,
33+ 'starttime': script_result.starttime,
34+ 'endtime': script_result.endtime,
35+ 'estimated_runtime': script_result.estimated_runtime,
36 'parameters': script_result.parameters,
37 'script_id': script_result.script_id,
38 'script_revision_id': script_result.script_version_id,
39@@ -378,6 +384,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser):
40 'started': fmt_time(script_result.started),
41 'ended': fmt_time(script_result.ended),
42 'runtime': script_result.runtime,
43+ 'starttime': script_result.starttime,
44+ 'endtime': script_result.endtime,
45+ 'estimated_runtime': script_result.estimated_runtime,
46 'parameters': script_result.parameters,
47 'script_id': script_result.script_id,
48 'script_revision_id': script_result.script_version_id,
49diff --git a/src/maasserver/static/js/angular/directives/script_runtime.js b/src/maasserver/static/js/angular/directives/script_runtime.js
50new file mode 100644
51index 0000000..3a198c3
52--- /dev/null
53+++ b/src/maasserver/static/js/angular/directives/script_runtime.js
54@@ -0,0 +1,57 @@
55+/* Copyright 2017 Canonical Ltd. This software is licensed under the
56+ * GNU Affero General Public License version 3 (see the file LICENSE).
57+ *
58+ * Script runtime counter directive.
59+ */
60+
61+angular.module('MAAS').run(['$templateCache', function ($templateCache) {
62+ // Inject the script_runtime.html into the template cache.
63+ $templateCache.put('directive/templates/script_runtime.html', [
64+ '<span data-ng-if="(scriptStatus === 1 || scriptStatus === 7) &&',
65+ " estimatedRunTime !== 'Unknown'" + '">{{counter}} of ',
66+ '~{{estimatedRunTime}}</span>',
67+ '<span data-ng-if="(scriptStatus === 1 || scriptStatus === 7) &&',
68+ " estimatedRunTime == 'Unknown'" + '">{{counter}}</span>',
69+ '<span data-ng-if="scriptStatus === 0 && estimatedRunTime !== ',
70+ "'Unknown'" + '">~{{estimatedRunTime}}</span>',
71+ '<span data-ng-if="scriptStatus !== 0 && scriptStatus !== 1 ',
72+ '&& scriptStatus !== 7">{{runTime}}</span>'
73+ ].join(''));
74+}]);
75+
76+angular.module('MAAS').directive('maasScriptRunTime', function() {
77+ return {
78+ restrict: "A",
79+ require: ["startTime", "runTime", "estimatedRunTime", "scriptStatus"],
80+ scope: {
81+ startTime: '=',
82+ runTime: '@',
83+ estimatedRunTime: '@',
84+ scriptStatus: '='
85+ },
86+ templateUrl: 'directive/templates/script_runtime.html',
87+ controller: function($scope, $interval) {
88+ $scope.counter = "0:00:00";
89+
90+ function incrementCounter() {
91+ if(($scope.scriptStatus === 1 || $scope.scriptStatus === 7) &&
92+ $scope.startTime) {
93+ var date = new Date(null);
94+ date.setSeconds((Date.now()/1000) - $scope.startTime);
95+ $scope.counter = date.toISOString().substr(11, 8);
96+ if($scope.counter.indexOf('00:') === 0) {
97+ $scope.counter = $scope.counter.substr(1);
98+ }
99+ }
100+ }
101+
102+ // Update the counter on init, start the interval and stop it when
103+ // the directive is destroyed.
104+ incrementCounter();
105+ var promise = $interval(incrementCounter, 1000);
106+ $scope.$on('$destroy', function() {
107+ $interval.cancel(promise);
108+ });
109+ }
110+ };
111+});
112diff --git a/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js b/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js
113new file mode 100644
114index 0000000..5839f3f
115--- /dev/null
116+++ b/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js
117@@ -0,0 +1,89 @@
118+/* Copyright 2017 Canonical Ltd. This software is licensed under the
119+ * GNU Affero General Public License version 3 (see the file LICENSE).
120+ *
121+ * Unit tests for script runtime directive.
122+ */
123+
124+describe("maasScriptRunTime", function() {
125+
126+ // Load the MAAS module.
127+ beforeEach(module("MAAS"));
128+
129+ // Create a new scope before each test.
130+ var $scope, $interval;
131+ beforeEach(inject(function($rootScope, $injector) {
132+ $interval = $injector.get('$interval');
133+ $scope = $rootScope.$new();
134+ $scope.startTime = null;
135+ $scope.runTime = null;
136+ $scope.estimatedRunTime = null;
137+ $scope.scriptStatus = null;
138+ }));
139+
140+ // Return the compiled directive.
141+ function compileDirective(
142+ startTime, runTime, estimatedRunTime, scriptStatus) {
143+ var directive;
144+ var html = '<span data-maas-script-run-time="script-runtime" ' +
145+ 'data-start-time="' + startTime + '" data-run-time="' +
146+ runTime + '" data-estimated-run-time="' + estimatedRunTime +
147+ '" data-script-status="' + scriptStatus + '"></span>';
148+
149+ // Compile the directive.
150+ inject(function($compile) {
151+ directive = $compile(html)($scope);
152+ });
153+
154+ // Perform the digest cycle to finish the compile.
155+ $scope.$digest();
156+ return directive;
157+ }
158+
159+ it('should have span element', function () {
160+ var startTime = Date.now() / 1000;
161+ var runTime = '0:00:30';
162+ var estimatedRunTime = '0:00:35';
163+ var scriptStatus = 7;
164+ var directive = compileDirective(
165+ startTime, runTime, estimatedRunTime, scriptStatus);
166+ var spanElement = directive.find('span');
167+ expect(spanElement).toBeDefined();
168+ expect(spanElement.text()).toEqual('0:00:00 of ~' + estimatedRunTime);
169+ });
170+
171+ it('should have applied template', function () {
172+ var startTime = Date.now() / 1000;
173+ var runTime = '0:00:30';
174+ var estimatedRunTime = '0:00:35';
175+ var scriptStatus = 7;
176+ var directive = compileDirective(
177+ startTime, runTime, estimatedRunTime, scriptStatus);
178+ expect(directive.html()).not.toEqual('');
179+ });
180+
181+ it('should counter based on passed time', function () {
182+ var startTime = (Date.now() / 1000) - 5; // 5 seconds
183+ var runTime = '0:00:30';
184+ var estimatedRunTime = '0:00:35';
185+ var scriptStatus = 7;
186+ var directive = compileDirective(
187+ startTime, runTime, estimatedRunTime, scriptStatus);
188+ var spanElement = directive.find('span');
189+ expect(spanElement).toBeDefined();
190+ expect(spanElement.text()).toEqual('0:00:05 of ~' + estimatedRunTime);
191+ });
192+
193+ it('counter updated based on passed time not $interval', function () {
194+ var startTime = (Date.now() / 1000) - 5; // 5 seconds
195+ var runTime = '0:00:30';
196+ var estimatedRunTime = '0:00:35';
197+ var scriptStatus = 7;
198+ var directive = compileDirective(
199+ startTime, runTime, estimatedRunTime, scriptStatus);
200+ // Flush should not cause the passed time to change.
201+ $interval.flush(1000);
202+ var spanElement = directive.find('span');
203+ expect(spanElement).toBeDefined();
204+ expect(spanElement.text()).toEqual('0:00:05 of ~' + estimatedRunTime);
205+ });
206+});
207diff --git a/src/maasserver/static/partials/script-results-list.html b/src/maasserver/static/partials/script-results-list.html
208index 974e44f..4fd4130 100644
209--- a/src/maasserver/static/partials/script-results-list.html
210+++ b/src/maasserver/static/partials/script-results-list.html
211@@ -13,9 +13,9 @@
212 <div class="table__header table-col--2 u-padding--left-none"></div>
213 <div class="table__header table-col--24">Name</div>
214 <div class="table__header table-col--24">Tags</div>
215- <div class="table__header table-col--10">Runtime</div>
216+ <div class="table__header table-col--15">Runtime</div>
217 <div class="table__header table-col--20">Date</div>
218- <div class="table__header table-col--15">Result</div>
219+ <div class="table__header table-col--10">Result</div>
220 <div class="table__header table-col--5 u-align--right">Actions</div>
221 </div>
222 </header>
223@@ -30,9 +30,9 @@
224 <button class="icon u-margin--top-tiny u-float--right" data-ng-class="{'icon--open': !result.showing_results, 'icon--close': result.showing_results}" data-ng-if="!result.showing_history"></button>
225 </div>
226 <div class="table__data table-col--24" aria-label="Tags"><span data-ng-hide="result.showing_history">{$ result.tags $}</span></div>
227- <div class="table__data table-col--10" aria-label="Runtime"><span data-ng-hide="result.showing_history">{$ result.runtime $}</span></div>
228+ <div class="table__data table-col--15" aria-label="Runtime"><span data-ng-hide="result.showing_history" data-maas-script-run-time="script-runtime" data-start-time="result.starttime" data-run-time="{{result.runtime}}" data-estimated-run-time="{{result.estimated_runtime}}" data-script-status="result.status"></span></div>
229 <div class="table__data table-col--20" aria-label="Date"><span data-ng-hide="result.showing_history">{$ result.updated $}</span></div>
230- <div class="table__data table-col--15" aria-label="Status">
231+ <div class="table__data table-col--10" aria-label="Status">
232 <span data-ng-hide="result.showing_history">
233 <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)-->
234 {$ result.status_name $} <a data-ng-if="result.status === 2 || result.status === 3 || result.status === 4 || result.status === 6 || result.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ result.id $}">View log</a>
235@@ -69,11 +69,11 @@
236 <div class="table__data table-col--2 u-padding--left-none" aria-label="Status">
237 <span data-maas-script-status="script-status" data-script-status="item.status"></span>
238 </div>
239- <div class="table__data table-col--20" aria-label="Name">{$ result.name $}</div>
240- <div class="table__data table-col--25" aria-label="Tags">{$ result.tags $}</div>
241- <div class="table__data table-col--10" aria-label="Runtime">{$ item.runtime $}</div>
242+ <div class="table__data table-col--24" aria-label="Name">{$ result.name $}</div>
243+ <div class="table__data table-col--24" aria-label="Tags">{$ result.tags $}</div>
244+ <div class="table__data table-col--15" aria-label="Runtime"><span data-maas-script-run-time="script-runtime" data-start-time="item.starttime" data-run-time="{{item.runtime}}" data-estimated-run-time="{{item.estimated_runtime}}" data-script-status="item.status"></span></div>
245 <div class="table__data table-col--20" aria-label="Date">{$ item.updated $}</div>
246- <div class="table__data table-col--23" aria-label="Status">
247+ <div class="table__data table-col--10" aria-label="Status">
248 <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)-->
249 {$ item.status_name $} <a data-ng-if="item.status === 2 || item.status === 3 || item.status === 4 || item.status === 6 || item.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ item.id $}">View log</a>
250 </div>
251diff --git a/src/maasserver/views/combo.py b/src/maasserver/views/combo.py
252index 6eb2fd9..e8fc6e1 100755
253--- a/src/maasserver/views/combo.py
254+++ b/src/maasserver/views/combo.py
255@@ -128,6 +128,7 @@ MERGE_VIEWS = {
256 "js/angular/directives/release_name.js",
257 "js/angular/directives/release_options.js",
258 "js/angular/directives/script_results_list.js",
259+ "js/angular/directives/script_runtime.js",
260 "js/angular/directives/script_select.js",
261 "js/angular/directives/script_status.js",
262 "js/angular/directives/ssh_keys.js",
263diff --git a/src/maasserver/websockets/handlers/node.py b/src/maasserver/websockets/handlers/node.py
264index 7b719a4..95c70b7 100644
265--- a/src/maasserver/websockets/handlers/node.py
266+++ b/src/maasserver/websockets/handlers/node.py
267@@ -605,6 +605,9 @@ class NodeHandler(TimestampedModelHandler):
268 'started': dehydrate_datetime(script_result.started),
269 'ended': dehydrate_datetime(script_result.ended),
270 'runtime': script_result.runtime,
271+ 'starttime': script_result.starttime,
272+ 'endtime': script_result.endtime,
273+ 'estimated_runtime': script_result.estimated_runtime,
274 })
275 if (script_result.stderr != b'' and
276 script_set.result_type != RESULT_TYPE.TESTING):
277@@ -621,6 +624,9 @@ class NodeHandler(TimestampedModelHandler):
278 'started': dehydrate_datetime(script_result.started),
279 'ended': dehydrate_datetime(script_result.ended),
280 'runtime': script_result.runtime,
281+ 'starttime': script_result.starttime,
282+ 'endtime': script_result.endtime,
283+ 'estimated_runtime': script_result.estimated_runtime,
284 })
285 return sorted(ret, key=lambda i: i['name'])
286
287diff --git a/src/maasserver/websockets/handlers/node_result.py b/src/maasserver/websockets/handlers/node_result.py
288index a405d06..e6e516e 100644
289--- a/src/maasserver/websockets/handlers/node_result.py
290+++ b/src/maasserver/websockets/handlers/node_result.py
291@@ -76,6 +76,9 @@ class NodeResultHandler(TimestampedModelHandler):
292 data["name"] = obj.name
293 data["status_name"] = obj.status_name
294 data["runtime"] = obj.runtime
295+ data["starttime"] = obj.starttime
296+ data["endtime"] = obj.endtime
297+ data["estimated_runtime"] = obj.estimated_runtime
298 data["result_type"] = obj.script_set.result_type
299 if obj.script is not None:
300 data["hardware_type"] = obj.script.hardware_type
301@@ -92,6 +95,9 @@ class NodeResultHandler(TimestampedModelHandler):
302 "status": history.status,
303 "status_name": history.status_name,
304 "runtime": history.runtime,
305+ "starttime": history.starttime,
306+ "endtime": history.endtime,
307+ "estimated_runtime": history.estimated_runtime,
308 } for history in obj.history
309 ]
310 try:
311diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py
312index edebbea..5080cfa 100644
313--- a/src/maasserver/websockets/handlers/tests/test_machine.py
314+++ b/src/maasserver/websockets/handlers/tests/test_machine.py
315@@ -1277,6 +1277,9 @@ class TestMachineHandler(MAASServerTestCase):
316 'started': dehydrate_datetime(script_result.started),
317 'ended': dehydrate_datetime(script_result.ended),
318 'runtime': script_result.runtime,
319+ 'starttime': script_result.starttime,
320+ 'endtime': script_result.endtime,
321+ 'estimated_runtime': script_result.estimated_runtime,
322 },
323 {
324 'id': script_result.id,
325@@ -1292,6 +1295,9 @@ class TestMachineHandler(MAASServerTestCase):
326 'started': dehydrate_datetime(script_result.started),
327 'ended': dehydrate_datetime(script_result.ended),
328 'runtime': script_result.runtime,
329+ 'starttime': script_result.starttime,
330+ 'endtime': script_result.endtime,
331+ 'estimated_runtime': script_result.estimated_runtime,
332 }], handler.dehydrate_script_set(script_set))
333
334 def test_dehydrate_script_set_returns_output_if_stdout_empty(self):
335@@ -1318,6 +1324,9 @@ class TestMachineHandler(MAASServerTestCase):
336 'started': dehydrate_datetime(script_result.started),
337 'ended': dehydrate_datetime(script_result.ended),
338 'runtime': script_result.runtime,
339+ 'starttime': script_result.starttime,
340+ 'endtime': script_result.endtime,
341+ 'estimated_runtime': script_result.estimated_runtime,
342 }, handler.dehydrate_script_set(script_result.script_set)[0])
343
344 def test_dehydrate_script_set_returns_combined_for_testing(self):
345@@ -1341,6 +1350,9 @@ class TestMachineHandler(MAASServerTestCase):
346 'started': dehydrate_datetime(script_result.started),
347 'ended': dehydrate_datetime(script_result.ended),
348 'runtime': script_result.runtime,
349+ 'starttime': script_result.starttime,
350+ 'endtime': script_result.endtime,
351+ 'estimated_runtime': script_result.estimated_runtime,
352 }, handler.dehydrate_script_set(script_result.script_set)[0])
353
354 def test_dehydrate_script_set_status(self):
355diff --git a/src/maasserver/websockets/handlers/tests/test_node_result.py b/src/maasserver/websockets/handlers/tests/test_node_result.py
356index 0c3013e..5b3fc37 100644
357--- a/src/maasserver/websockets/handlers/tests/test_node_result.py
358+++ b/src/maasserver/websockets/handlers/tests/test_node_result.py
359@@ -42,6 +42,9 @@ class TestNodeResultHandler(MAASServerTestCase):
360 "started": dehydrate_datetime(script_result.started),
361 "ended": dehydrate_datetime(script_result.ended),
362 "runtime": script_result.runtime,
363+ "starttime": script_result.starttime,
364+ "endtime": script_result.endtime,
365+ "estimated_runtime": script_result.estimated_runtime,
366 "name": script_result.name,
367 "result_type": script_result.script_set.result_type,
368 "hardware_type": script_result.script.hardware_type,
369@@ -52,6 +55,9 @@ class TestNodeResultHandler(MAASServerTestCase):
370 "status": history.status,
371 "status_name": history.status_name,
372 "runtime": history.runtime,
373+ "starttime": history.starttime,
374+ "endtime": history.endtime,
375+ "estimated_runtime": history.estimated_runtime,
376 } for history in script_result.history],
377 "results": [{
378 "name": key,
379diff --git a/src/metadataserver/models/scriptresult.py b/src/metadataserver/models/scriptresult.py
380index 7620ddd..dccda0a 100644
381--- a/src/metadataserver/models/scriptresult.py
382+++ b/src/metadataserver/models/scriptresult.py
383@@ -117,6 +117,51 @@ class ScriptResult(CleanSave, TimestampedModel):
384 else:
385 return ''
386
387+ @property
388+ def starttime(self):
389+ if self.started is not None:
390+ return self.started.timestamp()
391+ else:
392+ return ''
393+
394+ @property
395+ def endtime(self):
396+ if self.ended is not None:
397+ return self.ended.timestamp()
398+ else:
399+ return ''
400+
401+ @property
402+ def estimated_runtime(self):
403+ # If there is a runtime the script has completed, no need to calculate
404+ # an estimate.
405+ if self.runtime != '':
406+ return self.runtime
407+ runtime = None
408+ # Get an estimated runtime from previous runs.
409+ for script_result in self.history:
410+ # Only look at passed results when calculating an estimated
411+ # runtime. Failed results may take longer or shorter than
412+ # average. Don't use self.history.filter for this as the now
413+ # cached history list may be used elsewhere.
414+ if script_result.status != SCRIPT_STATUS.PASSED:
415+ continue
416+ previous_runtime = script_result.ended - script_result.started
417+ if runtime is None:
418+ runtime = previous_runtime
419+ else:
420+ runtime += previous_runtime
421+ runtime = runtime / 2
422+ if runtime is None:
423+ if self.script is not None and self.script.timeout != timedelta(0):
424+ # If there were no previous runs use the script's timeout.
425+ return str(self.script.timeout - timedelta(
426+ microseconds=self.script.timeout.microseconds))
427+ else:
428+ return 'Unknown'
429+ else:
430+ return str(runtime - timedelta(microseconds=runtime.microseconds))
431+
432 def __str__(self):
433 return "%s/%s" % (self.script_set.node.system_id, self.name)
434
435diff --git a/src/metadataserver/models/tests/test_scriptresult.py b/src/metadataserver/models/tests/test_scriptresult.py
436index ed8f41e..65f4fc7 100644
437--- a/src/metadataserver/models/tests/test_scriptresult.py
438+++ b/src/metadataserver/models/tests/test_scriptresult.py
439@@ -417,6 +417,80 @@ class TestScriptResult(MAASServerTestCase):
440 script_result = factory.make_ScriptResult(status=SCRIPT_STATUS.PENDING)
441 self.assertEquals('', script_result.runtime)
442
443+ def test_get_starttime(self):
444+ now = datetime.now()
445+ script_result = factory.make_ScriptResult(
446+ status=SCRIPT_STATUS.PASSED,
447+ started=now, ended=now)
448+ self.assertEquals(now.timestamp(), script_result.starttime)
449+
450+ def test_get_starttime_None(self):
451+ script_result = factory.make_ScriptResult(
452+ status=SCRIPT_STATUS.PENDING)
453+ self.assertEquals('', script_result.starttime)
454+
455+ def test_get_endtime(self):
456+ now = datetime.now()
457+ script_result = factory.make_ScriptResult(
458+ status=SCRIPT_STATUS.PASSED,
459+ started=now, ended=now)
460+ self.assertEquals(now.timestamp(), script_result.endtime)
461+
462+ def test_get_endtime_None(self):
463+ script_result = factory.make_ScriptResult(
464+ status=SCRIPT_STATUS.PENDING)
465+ self.assertEquals('', script_result.endtime)
466+
467+ def test_estimated_runtime_returns_set_runtime(self):
468+ now = datetime.now()
469+ script_result = factory.make_ScriptResult(
470+ status=SCRIPT_STATUS.PENDING,
471+ started=now, ended=(now + factory.make_timedelta()))
472+ self.assertEquals(
473+ script_result.runtime, script_result.estimated_runtime)
474+
475+ def test_estimated_runtime_returns_average_of_previous(self):
476+ script = factory.make_Script()
477+ script_set = factory.make_ScriptSet()
478+ old_results = [
479+ factory.make_ScriptResult(
480+ status=SCRIPT_STATUS.PASSED,
481+ script=script, script_set=script_set)
482+ for _ in range(10)
483+ ]
484+ factory.make_ScriptResult(
485+ status=SCRIPT_STATUS.FAILED,
486+ script=script, script_set=script_set)
487+ average_runtime = (old_results[9].ended - old_results[9].started)
488+ for result in reversed(old_results[:-1]):
489+ average_runtime += result.ended - result.started
490+ average_runtime = average_runtime / 2
491+ now = datetime.now()
492+ script_result = factory.make_ScriptResult(
493+ status=SCRIPT_STATUS.RUNNING, started=now,
494+ script=script, script_set=script_set)
495+ expected = str(
496+ average_runtime - timedelta(
497+ microseconds=average_runtime.microseconds))
498+ self.assertEquals(
499+ expected, script_result.estimated_runtime)
500+
501+ def test_estimated_runtime_uses_timeout(self):
502+ now = datetime.now()
503+ script_result = factory.make_ScriptResult(
504+ status=SCRIPT_STATUS.RUNNING, started=now)
505+ expected = str(script_result.script.timeout - timedelta(
506+ microseconds=script_result.script.timeout.microseconds))
507+ self.assertEquals(expected, script_result.estimated_runtime)
508+
509+ def test_estimated_runtime_returns_Unknown(self):
510+ now = datetime.now()
511+ script_result = factory.make_ScriptResult(
512+ status=SCRIPT_STATUS.RUNNING, started=now)
513+ script_result.script.timeout = timedelta(0)
514+ script_result.script.save()
515+ self.assertEquals("Unknown", script_result.estimated_runtime)
516+
517 def test_read_results(self):
518 results = {
519 'status': random.choice(

Subscribers

People subscribed via source and target branches