Merge lp:~javier.collado/utah/battery_measurements_config into lp:utah

Proposed by Javier Collado
Status: Merged
Approved by: Max Brustkern
Approved revision: 833
Merged at revision: 822
Proposed branch: lp:~javier.collado/utah/battery_measurements_config
Merge into: lp:utah
Diff against target: 477 lines (+147/-66)
5 files modified
utah/client/common.py (+59/-32)
utah/client/runner.py (+33/-12)
utah/client/testcase.py (+29/-13)
utah/client/tests/test_testcase.py (+1/-0)
utah/client/testsuite.py (+25/-9)
To merge this branch: bzr merge lp:~javier.collado/utah/battery_measurements_config
Reviewer Review Type Date Requested Status
Max Brustkern (community) Approve
Joe Talbott (community) Approve
Javier Collado (community) Needs Resubmitting
Review via email: mp+147791@code.launchpad.net

Description of the change

This branch adds a new boolean field `battery_measurements` that can be added
to the master runlist. When this field is set to `true` battery measurements
are taken if battery information is available. When this field is `false`,
which is the default value, no information will be gathered at all.

The initial design was to make it possible to specify `battery_measurements`
also in the test case control file. However, I think the additional complexity
needed to handle this wasn't really paying off. Anyway, if you think otherwise,
please let me know.

I'd like to add some documentation strings to the code that has been added
and/or updated, but if could let review the code as it is right now, it would
be great. Thanks.

To post a comment you must log in.
Revision history for this message
Andy Doan (doanac) wrote :

102 + kkkk"""

I you must be a VIM user :)

832. By Javier Collado

Removed vim side effect

Revision history for this message
Javier Collado (javier.collado) wrote :

@Andy

Indeed, I am. Thanks for pointing that out.

Revision history for this message
Max Brustkern (nuclearbob) wrote :

Now that line 93 exists, is line 90 still needed?

The rest of it looks good to me, but I haven't tested it yet.

833. By Javier Collado

Removed unused code

Revision history for this message
Javier Collado (javier.collado) wrote :

@Max

Fixed, I believe that was never really needed since it was set by the subclass
as an instance variable as it happens after these changes.

review: Needs Resubmitting
Revision history for this message
Max Brustkern (nuclearbob) wrote :

Cool. I'll try to get my laptop fired up today to test it, but if you've already tested it, it's probably good to go in.

Revision history for this message
Joe Talbott (joetalbott) wrote :

Looks good to me.

review: Approve
Revision history for this message
Max Brustkern (nuclearbob) wrote :

This looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'utah/client/common.py'
2--- utah/client/common.py 2013-02-06 14:16:20 +0000
3+++ utah/client/common.py 2013-02-15 08:57:19 +0000
4@@ -83,7 +83,8 @@
5
6 # Inspired by:
7 # http://stackoverflow.com/a/3326559
8-def run_cmd(command, cwd=None, timeout=0, cmd_type=CMD_TC_TEST, run_as=None):
9+def run_cmd(command, cwd=None, timeout=0, cmd_type=CMD_TC_TEST, run_as=None,
10+ battery_measurements=False):
11
12 if run_as is not None:
13 # add -n so sudo will not prompt for a password on the tty
14@@ -103,7 +104,9 @@
15 def alarm_handler(_signum, _frame):
16 raise TimeoutAlarm
17
18- start_battery = battery.get_data()
19+ if battery_measurements:
20+ start_battery = battery.get_data()
21+
22 start_time = datetime.datetime.now()
23 p = subprocess.Popen("{} {}".format(cmd_prefix, command),
24 shell=True, cwd=cwd,
25@@ -144,19 +147,23 @@
26 )
27
28 time_delta = datetime.datetime.now() - start_time
29- end_battery = battery.get_data()
30-
31- return make_result(command=command,
32- retcode=p.returncode,
33- stdout=stdout,
34- stderr=stderr,
35- start_time=start_time.strftime(DATE_FORMAT),
36- time_delta=str(time_delta),
37- cmd_type=cmd_type,
38- user=run_as,
39- start_battery=start_battery,
40- end_battery=end_battery,
41- )
42+
43+ kwargs = {'command': command,
44+ 'retcode': p.returncode,
45+ 'stdout': stdout,
46+ 'stderr': stderr,
47+ 'start_time': start_time.strftime(DATE_FORMAT),
48+ 'time_delta': str(time_delta),
49+ 'cmd_type': cmd_type,
50+ 'user': run_as,
51+ }
52+
53+ if battery_measurements:
54+ end_battery = battery.get_data()
55+ kwargs['start_battery'] = start_battery
56+ kwargs['end_battery'] = end_battery
57+
58+ return make_result(**kwargs)
59
60
61 def normalize_encoding(value, encoding='utf-8'):
62@@ -237,12 +244,24 @@
63 return data
64
65
66+# Validator that sets values to defaults as explained in:
67+# https://github.com/Julian/jsonschema/issues/4
68+class DefaultValidator(jsonschema.Validator):
69+ def validate_properties(self, properties, instance, schema):
70+ (super(DefaultValidator, self)
71+ .validate_properties(properties, instance, schema))
72+ instance.update((k, v['default'])
73+ for k, v in properties.iteritems()
74+ if k not in instance and 'default' in v)
75+
76+
77 def parse_control_file(filename, schema):
78 """
79 Parse a control file and check against the schema
80 """
81 control_data = parse_yaml_file(filename)
82- jsonschema.validate(control_data, schema)
83+ validator = DefaultValidator()
84+ validator.validate(control_data, schema)
85 return control_data
86
87
88@@ -453,19 +472,29 @@
89 """
90 Base class for Version Ccontrol System support.
91 """
92- repo = ""
93+
94+ def __init__(self, repo, destination='', battery_measurements=False):
95+ self.repo = repo
96+ self.destination = destination
97+ self.battery_measurements = battery_measurements
98
99 def get(self, directory=REPO_DEFAULT_DIR):
100 """
101 Method to retrieve a copy of the repository
102 """
103- return run_cmd(self.get_command, cwd=directory, cmd_type=CMD_TS_FETCH)
104+ return run_cmd(self.get_command,
105+ cwd=directory,
106+ cmd_type=CMD_TS_FETCH,
107+ battery_measurements=self.battery_measurements)
108
109 def revision(self, directory=REPO_DEFAULT_DIR):
110 """
111 Return the revision identifier.
112 """
113- return run_cmd(self.rev_command, cwd=directory, cmd_type=CMD_TS_FETCH)
114+ return run_cmd(self.rev_command,
115+ cwd=directory,
116+ cmd_type=CMD_TS_FETCH,
117+ battery_measurements=self.battery_measurements)
118
119 def get_revision(self, directory=REPO_DEFAULT_DIR):
120 """
121@@ -487,23 +516,23 @@
122 bazaar handler.
123 """
124
125- def __init__(self, repo, branch=True, options="", destination=""):
126- self.repo = repo
127+ def __init__(self, repo, branch=True, options="", destination="",
128+ **kwargs):
129+ super(BzrHandler, self).__init__(repo, destination, **kwargs)
130 self.options = options
131- self.destination = destination
132
133 if branch:
134 self.get_command = "bzr branch {} {} {}".format(
135 options,
136 self.repo,
137- destination,
138+ self.destination,
139 )
140
141 self.rev_command = "bzr revno"
142 else:
143 self.get_command = "bzr export {} {} {}".format(
144 options,
145- destination,
146+ self.destination,
147 self.repo,
148 )
149
150@@ -515,15 +544,14 @@
151 git handler.
152 """
153
154- def __init__(self, repo, options="", destination=""):
155- self.repo = repo
156+ def __init__(self, repo, options="", destination="", **kwargs):
157+ super(GitHandler, self).__init__(repo, destination, **kwargs)
158 self.options = options
159- self.destination = destination
160
161 self.get_command = "git clone {} {} {}".format(
162 options,
163 self.repo,
164- destination,
165+ self.destination,
166 )
167
168 self.rev_command = "git rev-parse HEAD"
169@@ -532,13 +560,12 @@
170 class DevHandler(VCSHandler):
171 """ Copy from a local directory for development. """
172
173- def __init__(self, repo, destination="."):
174- self.repo = repo
175- self.destination = destination
176+ def __init__(self, repo, destination=".", **kwargs):
177+ super(DevHandler, self).__init__(repo, destination, **kwargs)
178
179 self.get_command = "cp -r {} {}".format(
180 self.repo,
181- destination,
182+ self.destination,
183 )
184
185 self.rev_command = "echo 'DEVELOPMENT'"
186
187=== modified file 'utah/client/runner.py'
188--- utah/client/runner.py 2013-02-13 19:36:40 +0000
189+++ utah/client/runner.py 2013-02-15 08:57:19 +0000
190@@ -13,7 +13,10 @@
191 # You should have received a copy of the GNU General Public License along
192 # with this program. If not, see <http://www.gnu.org/licenses/>.
193
194-from utah.client.common import DEFAULT_TSLIST
195+from utah.client.common import (
196+ DEFAULT_TSLIST,
197+ DefaultValidator,
198+)
199 from utah.client.result import Result
200 from utah.client.testsuite import TestSuite
201 from utah.client.state_agent import StateAgentYAML
202@@ -130,6 +133,10 @@
203 "type": [MASTER_RUNLIST_SCHEMA_ORIG],
204 "required": True,
205 },
206+ 'battery_measurements': {
207+ 'type': 'boolean',
208+ 'default': False,
209+ }
210 }
211 }
212
213@@ -256,7 +263,8 @@
214 """
215 Run the test suites we've parsed.
216 """
217- self.result.start_battery = battery.get_data()
218+ if self.battery_measurements:
219+ self.result.start_battery = battery.get_data()
220
221 # Return value to indicate whether processing of a Runner should
222 # continue. This is to avoid a shutdown race on reboot cases.
223@@ -279,7 +287,8 @@
224 self.status = "DONE"
225 self.save_state()
226
227- self.result.end_battery = battery.get_data()
228+ if self.battery_measurements:
229+ self.result.end_battery = battery.get_data()
230 return self.process_results()
231
232 def add_suite(self, suite):
233@@ -378,8 +387,9 @@
234 raise exceptions.MissingFile(runlist)
235
236 data = parse_yaml_file(local_filename)
237+ validator = DefaultValidator()
238 try:
239- jsonschema.validate(data, self.MASTER_RUNLIST_SCHEMA)
240+ validator.validate(data, self.MASTER_RUNLIST_SCHEMA)
241 except jsonschema.ValidationError as exception:
242 raise exceptions.ValidationError(
243 'Master runlist failed to validate: {!r}\n'
244@@ -398,6 +408,8 @@
245 if 'publish' in data:
246 self.result.publish = data['publish']
247
248+ self.battery_measurements = data['battery_measurements']
249+
250 seen = []
251
252 orig_dir = os.getcwd()
253@@ -421,15 +433,20 @@
254 fetch_location = suite['fetch_location']
255
256 name = suite['name']
257+ kwargs = {'repo': fetch_location,
258+ 'destination': name,
259+ 'battery_measurements': self.battery_measurements,
260+ }
261 if fetch_method == 'bzr':
262- vcs_handler = BzrHandler(repo=fetch_location, destination=name)
263+ vcs_class = BzrHandler
264 elif fetch_method == 'bzr-export':
265- vcs_handler = BzrHandler(branch=False,
266- repo=fetch_location, destination=name)
267+ vcs_class = BzrHandler
268+ kwargs['branch'] = False
269 elif fetch_method == 'dev':
270- vcs_handler = DevHandler(repo=fetch_location, destination=name)
271+ vcs_class = DevHandler
272 else:
273- vcs_handler = GitHandler(repo=fetch_location, destination=name)
274+ vcs_class = GitHandler
275+ vcs_handler = vcs_class(**kwargs)
276
277 includes = suite.get('include_tests')
278 excludes = suite.get('exclude_tests')
279@@ -483,10 +500,14 @@
280 self.fetched_suites.append(name)
281
282 # Create a TestSuite
283- s = TestSuite(name=name, runlist_file=suite_runlist,
284- includes=includes, excludes=excludes,
285- result=self.result, path=self.testsuitedir,
286+ s = TestSuite(name=name,
287+ runlist_file=suite_runlist,
288+ includes=includes,
289+ excludes=excludes,
290+ result=self.result,
291+ path=self.testsuitedir,
292 timeout=self.timeout,
293+ battery_measurements=self.battery_measurements,
294 _save_state_callback=self.save_state,
295 _reboot_callback=self.reboot)
296 self.add_suite(s)
297
298=== modified file 'utah/client/testcase.py'
299--- utah/client/testcase.py 2012-12-08 02:10:12 +0000
300+++ utah/client/testcase.py 2013-02-15 08:57:19 +0000
301@@ -94,6 +94,7 @@
302 }
303
304 def __init__(self, name, path, result, command=None, timeout=None,
305+ battery_measurements=False,
306 _control_data=None, _save_state_callback=None,
307 _reboot_callback=None):
308 """
309@@ -108,6 +109,7 @@
310 self.results = []
311 self.result = result
312 self.timeout = timeout
313+ self.battery_measurements = battery_measurements
314 self.command = command
315 self.reboot_callback = _reboot_callback
316 self.run_as = None
317@@ -170,8 +172,12 @@
318 """
319 if self.status == 'NOTRUN':
320 self.set_status('BUILD')
321- result.add_result(run_cmd(self.build_cmd, cwd=self.working_dir,
322- cmd_type=CMD_TC_BUILD))
323+ cmd_result = run_cmd(
324+ self.build_cmd,
325+ cwd=self.working_dir,
326+ cmd_type=CMD_TC_BUILD,
327+ battery_measurements=self.battery_measurements)
328+ result.add_result(cmd_result)
329
330 def setup(self, result):
331 """
332@@ -179,8 +185,12 @@
333 """
334 if self.status == 'BUILD' and result.status == 'PASS':
335 self.set_status('SETUP')
336- result.add_result(run_cmd(self.tc_setup, cwd=self.working_dir,
337- cmd_type=CMD_TC_SETUP))
338+ cmd_result = run_cmd(
339+ self.tc_setup,
340+ cwd=self.working_dir,
341+ cmd_type=CMD_TC_SETUP,
342+ battery_measurements=self.battery_measurements)
343+ result.add_result(cmd_result)
344
345 def cleanup(self, result):
346 """
347@@ -188,8 +198,12 @@
348 """
349 if self.status == 'RUN':
350 self.set_status('CLEANUP')
351- result.add_result(run_cmd(self.tc_cleanup, cwd=self.working_dir,
352- cmd_type=CMD_TC_CLEANUP))
353+ cmd_result = run_cmd(
354+ self.tc_cleanup,
355+ cwd=self.working_dir,
356+ cmd_type=CMD_TC_CLEANUP,
357+ battery_measurements=self.battery_measurements)
358+ result.add_result(cmd_result)
359
360 def run(self):
361 """
362@@ -244,13 +258,15 @@
363 'action': self.action,
364 'expected_results': self.expected_results,
365 }
366- result.add_result(run_cmd(self.command,
367- timeout=timeout,
368- cwd=self.working_dir,
369- cmd_type=CMD_TC_TEST,
370- run_as=self.run_as,
371- ),
372- extra_info=extra_info)
373+ cmd_result = run_cmd(
374+ self.command,
375+ timeout=timeout,
376+ cwd=self.working_dir,
377+ cmd_type=CMD_TC_TEST,
378+ run_as=self.run_as,
379+ battery_measurements=self.battery_measurements,
380+ )
381+ result.add_result(cmd_result, extra_info=extra_info)
382
383 # only if we haven't failed or errored so far do
384 # we set the summary for the testcase.
385
386=== modified file 'utah/client/tests/test_testcase.py'
387--- utah/client/tests/test_testcase.py 2012-12-11 09:47:51 +0000
388+++ utah/client/tests/test_testcase.py 2013-02-15 08:57:19 +0000
389@@ -118,6 +118,7 @@
390 'dependencies': 'n/a',
391 'action': 'n/a',
392 'expected_results': 'n/a',
393+ 'battery_measurements': False,
394 }
395
396 result = ResultYAML()
397
398=== modified file 'utah/client/testsuite.py'
399--- utah/client/testsuite.py 2012-12-08 02:10:12 +0000
400+++ utah/client/testsuite.py 2013-02-15 08:57:19 +0000
401@@ -82,7 +82,7 @@
402
403 def __init__(self, name, path, result, runlist_file=DEFAULT_TSLIST,
404 control_file=DEFAULT_TSCONTROL, includes=None, excludes=None,
405- timeout=None,
406+ timeout=None, battery_measurements=False,
407 _control_data=None, _runlist_data=None,
408 _save_state_callback=None, _reboot_callback=None):
409
410@@ -92,6 +92,7 @@
411 self.failures = 0
412 self.errors = 0
413 self.timeout = timeout
414+ self.battery_measurements = battery_measurements
415
416 # work from within the testsuite's directory.
417 # eg. /var/lib/utah/examples
418@@ -164,9 +165,12 @@
419
420 command = test.get('command')
421
422- tc = TestCase(name=name, path=test_path,
423- command=command, timeout=self.timeout,
424+ tc = TestCase(name=name,
425+ path=test_path,
426+ command=command,
427+ timeout=self.timeout,
428 result=self.result,
429+ battery_measurements=self.battery_measurements,
430 _save_state_callback=self.save_state_callback,
431 _reboot_callback=self.reboot_callback)
432 if 'overrides' in test:
433@@ -193,8 +197,12 @@
434 """
435 if self.status == 'NOTRUN':
436 self.set_status('BUILD')
437- result.add_result(run_cmd(self.build_cmd, cwd=self.name,
438- cmd_type=CMD_TS_BUILD))
439+ cmd_result = run_cmd(
440+ self.build_cmd,
441+ cwd=self.name,
442+ cmd_type=CMD_TS_BUILD,
443+ battery_measurements=self.battery_measurements)
444+ result.add_result(cmd_result)
445
446 def setup(self, result):
447 """
448@@ -202,8 +210,12 @@
449 """
450 if self.status == 'BUILD' and result.status == 'PASS':
451 self.set_status('SETUP')
452- result.add_result(run_cmd(self.ts_setup, cwd=self.name,
453- cmd_type=CMD_TS_SETUP))
454+ cmd_result = run_cmd(
455+ self.ts_setup,
456+ cwd=self.name,
457+ cmd_type=CMD_TS_SETUP,
458+ battery_measurements=self.battery_measurements)
459+ result.add_result(cmd_result)
460
461 def cleanup(self, result):
462 """
463@@ -211,8 +223,12 @@
464 """
465 if self.status == 'INPROGRESS':
466 self.set_status('CLEANUP')
467- result.add_result(run_cmd(self.ts_cleanup, cwd=self.name,
468- cmd_type=CMD_TS_CLEANUP))
469+ cmd_result = run_cmd(
470+ self.ts_cleanup,
471+ cwd=self.name,
472+ cmd_type=CMD_TS_CLEANUP,
473+ battery_measurements=self.battery_measurements)
474+ result.add_result(cmd_result)
475
476 def run(self):
477 """

Subscribers

People subscribed via source and target branches