| OLD | NEW |
| 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 # DESCRIPTION : | 6 # DESCRIPTION : |
| 7 # | 7 # |
| 8 # This library provides common types and routines for the factory ui | 8 # This library provides common types and routines for the factory ui |
| 9 # infrastructure. This library explicitly does not import gtk, to | 9 # infrastructure. This library explicitly does not import gtk, to |
| 10 # allow its use by the autotest control process. | 10 # allow its use by the autotest control process. |
| 11 | 11 |
| 12 | 12 |
| 13 import gobject |
| 14 import signal |
| 13 import subprocess | 15 import subprocess |
| 14 import sys | 16 import sys |
| 15 import time | 17 import time |
| 16 | 18 |
| 17 | 19 |
| 18 ACTIVE = 'ACTIVE' | 20 ACTIVE = 'ACTIVE' |
| 19 PASSED = 'PASS' | 21 PASSED = 'PASS' |
| 20 FAILED = 'FAIL' | 22 FAILED = 'FAIL' |
| 21 UNTESTED = 'UNTESTED' | 23 UNTESTED = 'UNTESTED' |
| 22 | 24 |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 def read_new_data(self): | 201 def read_new_data(self): |
| 200 with open(self._status_file_path) as file: | 202 with open(self._status_file_path) as file: |
| 201 file.seek(self._status_file_pos) | 203 file.seek(self._status_file_pos) |
| 202 for line in file: | 204 for line in file: |
| 203 cols = line.strip().split('\t') + [''] | 205 cols = line.strip().split('\t') + [''] |
| 204 code = cols[0] | 206 code = cols[0] |
| 205 test_id = cols[1] | 207 test_id = cols[1] |
| 206 if code not in STATUS_CODE_MAP or test_id == '----': | 208 if code not in STATUS_CODE_MAP or test_id == '----': |
| 207 continue | 209 continue |
| 208 status = STATUS_CODE_MAP[code] | 210 status = STATUS_CODE_MAP[code] |
| 211 log('cols = %s' % repr(cols)) |
| 209 error_msg = status == FAILED and cols[len(cols) - 2] or None | 212 error_msg = status == FAILED and cols[len(cols) - 2] or None |
| 210 log('reading code = %s, test_id = %s, error_msg = "%s"' | 213 log('reading code = %s, test_id = %s, error_msg = "%s"' |
| 211 % (code, test_id, error_msg)) | 214 % (code, test_id, error_msg)) |
| 212 autotest_name, _, tag = test_id.rpartition('.') | 215 autotest_name, _, tag = test_id.rpartition('.') |
| 213 tag_prefix, _, count = tag.rpartition('_') | 216 tag_prefix, _, count = tag.rpartition('_') |
| 214 test = self.test_db.get_test_by_unique_details( | 217 test = self.test_db.get_test_by_unique_details( |
| 215 autotest_name, tag_prefix) | 218 autotest_name, tag_prefix) |
| 216 if test is None: | 219 if test is None: |
| 217 log('ignoring update (%s) for test "%s" "%s"' % | 220 log('ignoring update (%s) for test "%s" "%s"' % |
| 218 (status, autotest_name, tag_prefix)) | 221 (status, autotest_name, tag_prefix)) |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 file.seek(self._log_file_pos) | 295 file.seek(self._log_file_pos) |
| 293 for line in file: | 296 for line in file: |
| 294 parts = line.rsplit(DATA_PREFIX, 1) | 297 parts = line.rsplit(DATA_PREFIX, 1) |
| 295 if not len(parts) == 2: | 298 if not len(parts) == 2: |
| 296 continue | 299 continue |
| 297 key, raw_value = parts.pop().strip().split('=', 1) | 300 key, raw_value = parts.pop().strip().split('=', 1) |
| 298 log('updating shared_dict[%s]=%s' % (key, raw_value)) | 301 log('updating shared_dict[%s]=%s' % (key, raw_value)) |
| 299 self.shared_dict[key] = eval(raw_value) | 302 self.shared_dict[key] = eval(raw_value) |
| 300 self._log_file_pos = file.tell() | 303 self._log_file_pos = file.tell() |
| 301 | 304 |
| 302 | 305 def get(self, key): |
| 303 class UiClient: | 306 return self.shared_dict.get(key) |
| 304 '''Support communication with the factory_ui process. To simplify | |
| 305 surrounding code, this communication is an exchange of well formed | |
| 306 python expressions. Basically send wraps its arguments in a call | |
| 307 to repr() and recv calls eval() to re-generate the python data.''' | |
| 308 | |
| 309 def __init__(self, test_list, factory_ui_path, status_file_path): | |
| 310 self._proc = subprocess.Popen(factory_ui_path, | |
| 311 stdin=subprocess.PIPE, | |
| 312 stdout=subprocess.PIPE) | |
| 313 self.send(test_list) | |
| 314 self.send(status_file_path) | |
| 315 self.test_widget_size = self.recv() | |
| 316 log('control received test_widget_size = %s' % | |
| 317 repr(self.test_widget_size)) | |
| 318 | |
| 319 def __del__(self): | |
| 320 log('control deleting factory_ui subprocess') | |
| 321 self._proc.terminate() | |
| 322 time.sleep(1) | |
| 323 if self._proc.poll() is None: | |
| 324 self._proc.kill() | |
| 325 | |
| 326 def send(self, x=None): | |
| 327 print >> self._proc.stdin, repr(x) | |
| 328 self._proc.stdin.flush() | |
| 329 | |
| 330 def recv(self): | |
| 331 return eval(self._proc.stdout.readline().rstrip()) | |
| 332 | 307 |
| 333 | 308 |
| 334 class ControlState: | 309 class ControlState: |
| 335 | 310 |
| 336 def __init__(self, job, test_list, ui, status_map, status_file_path): | 311 def __init__(self, job, test_list, status_map, status_file_path, nuke_fn): |
| 337 self._job = job | 312 self._job = job |
| 338 self._status_map = status_map | 313 self._status_map = status_map |
| 339 self._log_data = LogData() | 314 self._log_data = LogData() |
| 340 self._std_dargs = { | 315 self._std_dargs = { |
| 341 'test_widget_size': ui.test_widget_size, | |
| 342 'trigger_set': status_map.test_db.kbd_shortcut_set, | |
| 343 'status_file_path' : status_file_path, | 316 'status_file_path' : status_file_path, |
| 344 'test_list': test_list} | 317 'test_list': test_list} |
| 318 self._nuke_fn = nuke_fn |
| 319 self.activated_kbd_shortcut_test = None |
| 320 signal.signal(signal.SIGUSR1, self.kill_current_test_callback) |
| 321 |
| 322 log('waiting for ui to come up...') |
| 323 while self._log_data.get('test_widget_size') is None: |
| 324 time.sleep(1) |
| 325 self._log_data.read_new_data() |
| 326 |
| 327 def kill_current_test_callback(self, signum, frame): |
| 328 self._log_data.read_new_data() |
| 329 active_test_data = self._log_data.get('active_test_data') |
| 330 log('KILLING active_test_data %s' % repr(active_test_data)) |
| 331 if active_test_data is not None: |
| 332 self._nuke_fn(*active_test_data) |
| 345 | 333 |
| 346 def run_test(self, test): | 334 def run_test(self, test): |
| 347 self._status_map.incr_count(test) | 335 self._status_map.incr_count(test) |
| 336 self._log_data.read_new_data() |
| 337 test_tag = self._status_map.lookup_tag(test) |
| 348 dargs = {} | 338 dargs = {} |
| 349 dargs.update(test.dargs) | 339 dargs.update(test.dargs) |
| 350 dargs.update(self._std_dargs) | 340 dargs.update(self._std_dargs) |
| 351 test_tag = self._status_map.lookup_tag(test) | |
| 352 dargs.update({'tag': test_tag, | 341 dargs.update({'tag': test_tag, |
| 353 'subtest_tag': test_tag, | 342 'subtest_tag': test_tag, |
| 354 'shared_dict': self._log_data.shared_dict}) | 343 'shared_dict': self._log_data.shared_dict}) |
| 344 |
| 345 self._job.factory_shared_dict = self._log_data.shared_dict |
| 346 |
| 347 log('control shared dict = %s' % repr(self._log_data.shared_dict)) |
| 348 |
| 355 if test.drop_caches: | 349 if test.drop_caches: |
| 356 self._job.drop_caches_between_iterations = True | 350 self._job.drop_caches_between_iterations = True |
| 351 self.activated_kbd_shortcut_test = None |
| 352 |
| 357 self._job.run_test(test.autotest_name, **dargs) | 353 self._job.run_test(test.autotest_name, **dargs) |
| 354 |
| 358 self._job.drop_caches_between_iterations = False | 355 self._job.drop_caches_between_iterations = False |
| 359 self._log_data.read_new_data() | 356 self._log_data.read_new_data() |
| 360 activated_ks = self._log_data.shared_dict.pop( | 357 kbd_shortcut = self._log_data.shared_dict.pop( |
| 361 'activated_kbd_shortcut', None) | 358 'activated_kbd_shortcut', None) |
| 362 lookup = self._status_map.test_db.get_test_by_kbd_shortcut | 359 if kbd_shortcut is not None: |
| 363 self.activated_kbd_shortcut_test = ( | 360 test_db = self._status_map.test_db |
| 364 activated_ks and lookup(activated_ks) or None) | 361 target_test = test_db.get_test_by_kbd_shortcut(kbd_shortcut) |
| 362 self.activated_kbd_shortcut_test = target_test |
| 363 log('kbd_shortcut %s -> %s)' % ( |
| 364 kbd_shortcut, test_db.get_unique_details(target_test))) |
| OLD | NEW |