Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 2 # | 2 # |
| 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 AUTHOR = "Chrome OS Team" | 7 AUTHOR = "Chrome OS Team" |
| 8 NAME = "Factory" | 8 NAME = "Factory" |
| 9 TIME = "LONG" | 9 TIME = "LONG" |
| 10 TEST_CATEGORY = "Functional" | 10 TEST_CATEGORY = "Functional" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 | 24 |
| 25 | 25 |
| 26 import subprocess | 26 import subprocess |
| 27 import sys | 27 import sys |
| 28 import time | 28 import time |
| 29 | 29 |
| 30 | 30 |
| 31 _FACTORY_LOG_PATH = '/var/log/factory.log' | 31 _FACTORY_LOG_PATH = '/var/log/factory.log' |
| 32 _RESULT_FILE_PATH = '/var/run/factory_test_result' | 32 _RESULT_FILE_PATH = '/var/run/factory_test_result' |
| 33 | 33 |
| 34 _REBOOT_SEQ_ITERATIONS = 2 | |
| 35 | |
| 34 | 36 |
| 35 def XXX_log(s): | 37 def XXX_log(s): |
| 36 print >> sys.stderr, '--- XXX : ' + s | 38 print >> sys.stderr, '--- XXX : ' + s |
| 37 | 39 |
| 38 | 40 |
| 39 # Hack to work around autotest's obsession with GRUB. | 41 # Hack to work around autotest's obsession with GRUB. |
| 40 job.bootloader.set_default = lambda x: None | 42 job.bootloader.set_default = lambda x: None |
| 41 job.bootloader.boot_once = lambda x: None | 43 job.bootloader.boot_once = lambda x: None |
| 42 | 44 |
| 43 | 45 |
| 44 class factory_ui: | |
| 45 '''Support communication with the factory_ui process. To simplify | |
| 46 surrounding code, this communication is an exchange of well formed | |
| 47 python expressions. Basically send wraps its arguments in a call | |
| 48 to repr() and recv calls eval() to re-generate the python data.''' | |
| 49 | |
| 50 def __init__(self, factory_ui_path): | |
| 51 self._proc = subprocess.Popen(factory_ui_path, | |
| 52 stdin=subprocess.PIPE, | |
| 53 stdout=subprocess.PIPE) | |
| 54 | |
| 55 def __del__(self): | |
| 56 XXX_log('factory_ui __del__') | |
| 57 self._proc.terminate() | |
| 58 time.sleep(1) | |
| 59 if self._proc.poll() is None: | |
| 60 self._proc.kill() | |
| 61 | |
| 62 def send(self, x): | |
| 63 print >> self._proc.stdin, repr(x) | |
| 64 self._proc.stdin.flush() | |
| 65 | |
| 66 def recv(self): | |
| 67 return eval(self._proc.stdout.readline().rstrip()) | |
| 68 | |
| 69 | |
| 70 # This is the definition of the test_data class, which holds | 46 # This is the definition of the test_data class, which holds |
| 71 # factory-specific information on the tests to be run. Specifically, | 47 # factory-specific information on the tests to be run. Specifically, |
| 72 # the order of items in the list reflect the order they are to be run | 48 # the order of items in the list reflect the order they are to be run |
| 73 # on the line. The label and trigger fields contain the description | 49 # on the line. The label and trigger fields contain the description |
| 74 # strings to be shown in the test control list of the UI. The trigger | 50 # strings to be shown in the test control list of the UI. The trigger |
| 75 # field specifies the keyboard shortcut to allow on-demain | 51 # field specifies the keyboard shortcut to allow on-demain |
| 76 # out-of-order test activation. The dargs field allows test specific | 52 # out-of-order test activation. The dargs field allows test specific |
| 77 # extra arguments. Note: this datastructure is defined as a string | 53 # extra arguments. Note: this datastructure is defined as a string |
| 78 # and exec()ed to allow it to be defined once here, but to be also | 54 # and exec()ed to allow it to be defined once here, but to be also |
| 79 # used by the factory_ui process. | 55 # used by the factory_ui process. |
| 80 | 56 |
| 81 test_data_class_def = ''' | 57 test_data_class_def = ''' |
| 82 class test_data: | 58 class test_data: |
| 83 | 59 |
| 84 def __init__(self, label_en='', label_zw='', formal_name='', | 60 def __init__(self, label_en='', label_zw='', formal_name=None, |
| 85 trigger=None, dargs={}): | 61 tag_prefix=None, trigger=None, automated_seq=[], dargs={}): |
| 86 self.__dict__.update(vars()) | 62 self.__dict__.update(vars()) |
| 87 | 63 |
| 88 def __repr__(self): | 64 def __repr__(self): |
| 89 d = ['%s=%s' % (l,repr(v)) | 65 d = ['%s=%s' % (l,repr(v)) |
| 90 for l,v in self.__dict__.items() | 66 for l,v in self.__dict__.items() |
| 91 if l != 'self'] | 67 if l != 'self'] |
| 92 c = ('%s' % self.__class__).rpartition('.')[2] | 68 c = ('%s' % self.__class__).rpartition('.')[2] |
| 93 return '%s(%s)' % (c, ','.join(d)) | 69 return '%s(%s)' % (c, ','.join(d)) |
| 94 ''' | 70 ''' |
| 95 exec(test_data_class_def) | 71 exec(test_data_class_def) |
| 96 | 72 |
| 97 | 73 |
| 98 test_list = [ | 74 test_list = [ |
| 99 test_data( | 75 test_data( |
| 100 label_en='dummy A', | 76 label_en='start', |
| 101 formal_name='factory_Dummy', | 77 formal_name='factory_Dummy', |
| 102 trigger='a', | 78 trigger='a', |
| 103 dargs={'msg':'Hello World'}), | 79 dargs={'quit_key':ord(' '), |
| 80 'msg':'Hit SPACE to start testing...\n按 "空白鍵" 開始測試...'}), | |
| 104 test_data( | 81 test_data( |
| 105 label_en='dummy B', | 82 label_en='run-in', |
| 106 formal_name='factory_Dummy', | 83 formal_name='step_runin', |
| 107 trigger='b', | 84 automated_seq=[ |
| 108 dargs={'msg':'Hello Factory'}), | 85 test_data( |
| 86 label_en='component validation', | |
| 87 formal_name='hardware_Components'), | |
| 88 test_data( | |
| 89 label_en='gpio switch check', | |
| 90 formal_name='hardware_GPIOSwitches'), | |
| 91 test_data( | |
| 92 label_en='system stress', | |
| 93 formal_name='hardware_SAT'), | |
|
Nick Sanders
2010/06/02 08:06:36
is there any way to get the test list specified in
Tammo Spalink
2010/06/07 03:40:21
You mean like scrape the names out from the step_r
| |
| 94 test_data( | |
| 95 label_en='reboot (%s times)' % _REBOOT_SEQ_ITERATIONS, | |
| 96 formal_name='factory_RebootStub')], | |
| 97 trigger='r'), | |
| 109 test_data( | 98 test_data( |
| 110 label_en='dummy C', | 99 label_en='camera', |
| 111 formal_name='factory_Dummy', | 100 formal_name='factory_Dummy', |
| 112 trigger='c', | 101 trigger='c', |
| 113 dargs={'msg':'Hello Mom...'}), | 102 dargs={'msg':'camera test, one day...'}), |
| 114 test_data( | 103 test_data( |
| 115 label_en='keyboard', | 104 label_en='keyboard', |
| 116 label_zw='鍵盤', | 105 label_zw='鍵盤', |
| 117 formal_name='factory_Keyboard', | 106 formal_name='factory_Keyboard', |
| 118 trigger='k', | 107 trigger='k', |
| 119 dargs={'layout':'en_us'}), | 108 dargs={'layout':'en_us'}), |
| 120 test_data( | 109 test_data( |
| 121 label_en='trackpad', | 110 label_en='trackpad', |
| 122 label_zw='觸控板', | 111 label_zw='觸控板', |
| 123 formal_name='hardware_Touchpad', | 112 formal_name='hardware_Touchpad', |
| 124 trigger='t'), | 113 trigger='t'), |
| 125 ] | 114 ] |
| 126 | 115 |
| 116 for test in test_list: | |
| 117 test.tag_prefix = test.trigger | |
| 118 for subtest in test.automated_seq: | |
| 119 subtest.tag_prefix = test.formal_name | |
| 127 | 120 |
| 128 test_map = dict((test.label_en, test) for test in test_list) | 121 def test_map_index(formal_name, tag_prefix): |
| 122 return formal_name + '.' + tag_prefix | |
| 129 | 123 |
| 124 test_map = dict((test_map_index(test.formal_name, test.tag_prefix), test) | |
| 125 for test in test_list) | |
| 130 | 126 |
| 131 trigger_set = set(test.trigger for test in test_list) | 127 trigger_set = set(test.trigger for test in test_list) |
| 132 | 128 |
| 133 | 129 |
| 134 def step_init(): | 130 class factory_ui: |
| 135 job.next_step([step_run_ui]) | 131 '''Support communication with the factory_ui process. To simplify |
| 136 step_run_ui() | 132 surrounding code, this communication is an exchange of well formed |
| 133 python expressions. Basically send wraps its arguments in a call | |
| 134 to repr() and recv calls eval() to re-generate the python data.''' | |
| 135 | |
| 136 def __init__(self, factory_ui_path): | |
| 137 self._proc = subprocess.Popen(factory_ui_path, | |
| 138 stdin=subprocess.PIPE, | |
| 139 stdout=subprocess.PIPE) | |
| 140 | |
| 141 def __del__(self): | |
| 142 XXX_log('control deleting factory_ui subprocess') | |
| 143 self._proc.terminate() | |
| 144 time.sleep(1) | |
| 145 if self._proc.poll() is None: | |
| 146 self._proc.kill() | |
| 147 | |
| 148 def send(self, x=None): | |
| 149 print >> self._proc.stdin, repr(x) | |
| 150 self._proc.stdin.flush() | |
| 151 | |
| 152 def send_cmd_next_test(self): | |
| 153 self.send(('next_test', None)) | |
| 154 | |
| 155 def send_cmd_switch_to(self, trigger): | |
| 156 self.send(('switch_to', trigger)) | |
| 157 | |
| 158 def recv(self): | |
| 159 return eval(self._proc.stdout.readline().rstrip()) | |
| 160 | |
| 161 def recv_target_test_update(self): | |
| 162 update = self.recv() | |
| 163 XXX_log('control recv target test %s' % repr(update)) | |
| 164 formal_name, tag_prefix, count = update | |
| 165 test = test_map.get(test_map_index(formal_name, tag_prefix), None) | |
| 166 if test is not None: | |
| 167 test.count = count | |
| 168 return test | |
| 137 | 169 |
| 138 | 170 |
| 139 def step_run_ui(): | 171 def step_reboot_seq(i, tag): |
| 172 if i < _REBOOT_SEQ_ITERATIONS: | |
| 173 job.next_step([step_reboot_seq, i + 1, tag]) | |
| 174 XXX_log('rebooting (iteration %d)' % i) | |
| 175 time.sleep(5) | |
| 176 job.reboot() | |
| 177 else: | |
| 178 job.run_test('factory_RebootStub', tag=tag) | |
| 179 step_init() | |
| 180 | |
| 181 | |
| 182 def step_runin(ui, tag): | |
| 183 job.run_test('hardware_Components', | |
| 184 approved_db='qualified_components', | |
| 185 tag=tag) | |
| 186 ui.send() | |
| 187 job.run_test('hardware_GPIOSwitches', tag=tag) | |
| 188 ui.send() | |
| 189 job.drop_caches_between_iterations = True | |
| 190 job.run_test('hardware_SAT', tag=tag) | |
| 191 job.drop_caches_between_iterations = False | |
| 192 ui.send() | |
| 193 step_reboot_seq(0, tag) | |
| 194 | |
| 195 | |
| 196 def step_init(): | |
| 197 | |
| 140 '''Launch the factory UI, which will then make decisions on which | 198 '''Launch the factory UI, which will then make decisions on which |
| 141 tests to run in which order. This is to support user driven | 199 tests to run in which order. This is to support user driven |
| 142 out-of-order test execution based on keyboard shortcuts. | 200 out-of-order test execution based on keyboard shortcuts. |
| 143 | 201 |
| 144 For each test, a trigger (possibly None) is communicated to the | 202 For each test, a trigger (possibly None) is communicated to the |
| 145 UI, which then replies with the test name and a count number that | 203 UI, which then replies with the test name and a count number that |
| 146 becomes the autotest tag to allow repeated test execution while | 204 becomes the autotest tag to allow repeated test execution while |
| 147 preserving logs. | 205 preserving logs. |
| 148 | 206 |
| 149 When the tests themselves run, they are expected to look for | 207 When the tests themselves run, they are expected to look for |
| 150 (using the factory_test library) keyboard events that match test | 208 (using the factory_test library) keyboard events that match test |
| 151 switching triggers. When a trigger happens, it should be written | 209 switching triggers. When a trigger happens, it should be written |
| 152 to the _RESULT_FILE_PATH, which will be read after the test | 210 to the _RESULT_FILE_PATH, which will be read after the test |
| 153 completed and the result comminicated onwards to the UI.''' | 211 completed and the result comminicated onwards to the UI.''' |
| 154 | 212 |
| 155 status_file_path = job.autodir + '/results/default/status' | 213 status_file_path = job.autodir + '/results/default/status' |
| 156 factory_ui_path = job.autodir + '/factory_ui' | 214 factory_ui_path = job.autodir + '/factory_ui' |
| 157 | 215 |
| 158 ui = factory_ui(factory_ui_path) | 216 ui = factory_ui(factory_ui_path) |
| 159 | 217 |
| 160 ui.send(test_data_class_def) | 218 ui.send(test_data_class_def) |
| 161 ui.send(test_list) | 219 ui.send(test_list) |
| 162 ui.send(status_file_path) | 220 ui.send(status_file_path) |
| 163 ui.send(_FACTORY_LOG_PATH) | 221 ui.send(_FACTORY_LOG_PATH) |
| 164 | 222 |
| 165 test_widget_size = ui.recv() | 223 test_widget_size = ui.recv() |
| 166 XXX_log('received test_widget_size = %s' % repr(test_widget_size)) | 224 XXX_log('received test_widget_size = %s' % repr(test_widget_size)) |
| 167 | 225 |
| 168 # Send initial 'None' trigger. | 226 ui.send_cmd_next_test() |
| 169 ui.send(None) | 227 test = ui.recv_target_test_update() |
| 170 | 228 |
| 171 while True: | 229 while test is not None: |
| 172 next_test_name, count = ui.recv() | 230 if test.automated_seq: |
| 231 tag = '%s_%s' % (test.formal_name, test.count) | |
| 232 exec('%s(ui, "%s")' % (test.formal_name, tag)) | |
| 233 result = None | |
| 234 else: | |
| 235 dargs = test.dargs | |
| 236 dargs.update({ | |
| 237 'tag': '%s_%s' % (test.tag_prefix, test.count), | |
| 238 'test_widget_size': test_widget_size, | |
| 239 'trigger_set': trigger_set, | |
| 240 'result_file_path': _RESULT_FILE_PATH}) | |
| 241 with open(_RESULT_FILE_PATH, 'w') as file: | |
| 242 file.write('None\n') | |
| 243 job.run_test(test.formal_name, **dargs) | |
| 244 with open(_RESULT_FILE_PATH, 'r') as file: | |
| 245 result = eval(file.readline()) | |
| 173 | 246 |
| 174 XXX_log('next_test_name = %s , count = %s' % (next_test_name, count)) | 247 if result is not None: |
| 175 if next_test_name is None: | 248 ui.send_cmd_switch_to(result) |
| 176 XXX_log('factory testing completed') | 249 else: |
| 177 break | 250 ui.send_cmd_next_test() |
| 178 | 251 |
| 179 test = test_map[next_test_name] | 252 test = ui.recv_target_test_update() |
| 180 dargs = test.dargs | |
| 181 dargs.update({ | |
| 182 'tag': str(count), | |
| 183 'test_widget_size': test_widget_size, | |
| 184 'trigger_set': trigger_set, | |
| 185 'result_file_path': _RESULT_FILE_PATH}) | |
| 186 | 253 |
| 187 with open(_RESULT_FILE_PATH, 'w') as file: | 254 XXX_log('factory testing completed') |
| 188 file.write('None\n') | |
| 189 job.run_test(test.formal_name, **dargs) | |
| 190 with open(_RESULT_FILE_PATH, 'r') as file: | |
| 191 result = eval(file.readline()) | |
| 192 ui.send(result) | |
| OLD | NEW |