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" |
11 TEST_CLASS = "suite" | 11 TEST_CLASS = "suite" |
12 TEST_TYPE = "client" | 12 TEST_TYPE = "client" |
13 | 13 |
14 DOC = """ | 14 DOC = """ |
15 This suite executed all of the factory tests, and incorporates an | 15 This suite executed all of the factory tests, and incorporates an |
16 X-windows GTK-based UI to highlight testing results and to allow | 16 X-windows GTK-based UI to highlight testing results and to allow |
17 factory operators to switch between tests, and to re-run tests, all | 17 factory operators to switch between tests, and to re-run tests, all |
18 on-demand via keyboard shortcuts. | 18 on-demand via keyboard shortcuts. |
19 | 19 |
20 The UI is implemented as a seperate process (see _FACTORY_UI_PATH), | 20 The UI is implemented as a seperate process (see _FACTORY_UI_PATH), |
21 which means that interprocess communication is needed between this | 21 which means that interprocess communication is needed between this |
22 control, the UI, and the tests (which are forked children of this | 22 control, the UI, and the tests (which are forked children of this |
23 control process). """ | 23 control process). """ |
24 | 24 |
25 | 25 |
| 26 import imp |
26 import subprocess | 27 import subprocess |
27 import sys | 28 import sys |
28 import time | 29 import time |
29 | 30 |
| 31 imp.load_source('common', job.autodir + '/bin/common.py') |
| 32 from autotest_lib.client.bin import factory |
30 | 33 |
31 _FACTORY_LOG_PATH = '/var/log/factory.log' | 34 |
32 _RESULT_FILE_PATH = '/var/run/factory_test_result' | 35 _STATUS_FILE_PATH = job.autodir + '/results/default/status' |
| 36 _FACTORY_UI_PATH = job.autodir + '/bin/factory_ui' |
| 37 |
33 | 38 |
34 _REBOOT_SEQ_ITERATIONS = 2 | 39 _REBOOT_SEQ_ITERATIONS = 2 |
35 | 40 |
36 | 41 |
37 def XXX_log(s): | |
38 print >> sys.stderr, 'FACTORY: ' + s | |
39 | |
40 | |
41 # Hack to work around autotest's obsession with GRUB. | 42 # Hack to work around autotest's obsession with GRUB. |
42 job.bootloader.set_default = lambda x: None | 43 job.bootloader.set_default = lambda x: None |
43 job.bootloader.boot_once = lambda x: None | 44 job.bootloader.boot_once = lambda x: None |
44 | 45 |
45 | 46 |
46 # This is the definition of the test_data class, which holds | |
47 # factory-specific information on the tests to be run. Specifically, | |
48 # the order of items in the list reflect the order they are to be run | |
49 # on the line. The label and trigger fields contain the description | |
50 # strings to be shown in the test control list of the UI. The trigger | |
51 # field specifies the keyboard shortcut to allow on-demain | |
52 # out-of-order test activation. The dargs field allows test specific | |
53 # extra arguments. Note: this datastructure is defined as a string | |
54 # and exec()ed to allow it to be defined once here, but to be also | |
55 # used by the factory_ui process. | |
56 | |
57 test_data_class_def = ''' | |
58 class test_data: | |
59 | |
60 def __init__(self, label_en='', label_zw='', formal_name=None, | |
61 tag_prefix=None, trigger=None, automated_seq=[], dargs={}): | |
62 self.__dict__.update(vars()) | |
63 | |
64 def __repr__(self): | |
65 d = ['%s=%s' % (l,repr(v)) | |
66 for l,v in self.__dict__.items() | |
67 if l != 'self'] | |
68 c = ('%s' % self.__class__).rpartition('.')[2] | |
69 return '%s(%s)' % (c, ','.join(d)) | |
70 ''' | |
71 exec(test_data_class_def) | |
72 | |
73 | |
74 test_list = [ | 47 test_list = [ |
75 test_data( | 48 factory.TestData( |
76 label_en='start', | 49 label_en='start', |
| 50 label_zw='開始', |
77 formal_name='factory_Dummy', | 51 formal_name='factory_Dummy', |
78 trigger='a', | 52 trigger='e', |
79 dargs={'quit_key':ord(' '), | 53 dargs={'quit_key':ord(' '), |
80 'msg':'Hit SPACE to start testing...\n按 "空白鍵" 開始測試...'}), | 54 'msg':'Hit SPACE to start testing...\n按 "空白鍵" 開始測試...'}), |
81 test_data( | 55 factory.TestData( |
82 label_en='sync', | 56 label_en='sync', |
| 57 label_zw='同步', |
83 formal_name='factory_ScriptWrapper', | 58 formal_name='factory_ScriptWrapper', |
84 trigger='s', | 59 trigger='s', |
85 dargs={'cmdline':'/usr/local/autotest/exscr'}), | 60 dargs={'cmdline': job.autodir + |
86 test_data( | 61 '/site_tests/factory_ScriptWrapper/dummy.sh'}), |
87 label_en='leds', | 62 factory.TestData( |
88 formal_name='factory_Dummy', | 63 label_en='run-in', |
89 trigger='l', | 64 label_zw='燒機測試', |
90 dargs={'msg':'LEDs test, one day...'}), | 65 formal_name='step_runin', |
91 test_data( | 66 automated_seq=[ |
92 label_en='usb', | 67 factory.TestData( |
93 formal_name='factory_ExternalStorage', | 68 label_en='component validation', |
94 trigger='u'), | 69 label_zw='元件驗證', |
95 test_data( | 70 formal_name='hardware_Components', |
96 label_en='display', | 71 dargs={'approved_db':'qualified_components'}), |
97 formal_name='factory_Display', | 72 factory.TestData( |
98 trigger='m'), | 73 label_en='gpio switch check', |
99 test_data( | 74 label_zw='檢查 gpio 開關', |
100 label_en='camera', | 75 formal_name='hardware_GPIOSwitches'), |
101 formal_name='factory_Dummy', | 76 factory.TestData( |
102 trigger='c', | 77 label_en='system stress', |
103 dargs={'msg':'camera test, one day...'}), | 78 label_zw='壓力測試', |
104 test_data( | 79 formal_name='hardware_SAT'), |
| 80 factory.TestData( |
| 81 label_en='reboot (%s times)' % _REBOOT_SEQ_ITERATIONS, |
| 82 label_zw='重新開機 (%s 次)' % _REBOOT_SEQ_ITERATIONS, |
| 83 formal_name='factory_RebootStub')], |
| 84 trigger='r'), |
| 85 factory.TestData( |
105 label_en='keyboard', | 86 label_en='keyboard', |
106 label_zw='鍵盤', | 87 label_zw='鍵盤', |
107 formal_name='factory_Keyboard', | 88 formal_name='factory_Keyboard', |
108 trigger='k', | 89 trigger='k', |
109 dargs={'layout':'en_us'}), | 90 dargs={'layout':'en_us'}), |
110 test_data( | 91 factory.TestData( |
111 label_en='touchpad', | 92 label_en='touchpad', |
112 label_zw='觸控板', | 93 label_zw='觸控板', |
113 formal_name='factory_Touchpad', | 94 formal_name='factory_Touchpad', |
114 trigger='t'), | 95 trigger='t'), |
115 test_data( | 96 factory.TestData( |
| 97 label_en='leds', |
| 98 label_zw='機身側燈', |
| 99 formal_name='factory_Dummy', |
| 100 trigger='l', |
| 101 dargs={'msg':'LEDs test, one day...'}), |
| 102 factory.TestData( |
| 103 label_en='display', |
| 104 label_zw='顯示', |
| 105 formal_name='factory_Display', |
| 106 trigger='m'), |
| 107 factory.TestData( |
| 108 label_en='camera', |
| 109 label_zw='相機', |
| 110 formal_name='factory_Dummy', |
| 111 trigger='c', |
| 112 dargs={'msg':'camera test, one day...'}), |
| 113 factory.TestData( |
| 114 label_en='audio', |
| 115 label_zw='聲音', |
| 116 formal_name='factory_Dummy', |
| 117 trigger='a', |
| 118 dargs={'msg':'audio test, one day...'}), |
| 119 factory.TestData( |
| 120 label_en='usb', |
| 121 formal_name='factory_ExternalStorage', |
| 122 trigger='u'), |
| 123 factory.TestData( |
| 124 label_en='sd', |
| 125 formal_name='factory_ExternalStorage', |
| 126 trigger='d'), |
| 127 factory.TestData( |
| 128 label_en='bluetooth', |
| 129 label_zw='藍牙', |
| 130 formal_name='factory_Dummy', |
| 131 trigger='o', |
| 132 dargs={'msg':'bluetooth test, one day...'}), |
| 133 factory.TestData( |
| 134 label_en='3g', |
| 135 label_zw='第三代', |
| 136 formal_name='factory_Dummy', |
| 137 trigger='g', |
| 138 dargs={'msg':'3g test, one day...'}), |
| 139 factory.TestData( |
| 140 label_en='wifi', |
| 141 label_zw='無線上網', |
| 142 formal_name='factory_Dummy', |
| 143 trigger='w', |
| 144 dargs={'msg':'wifi test, one day...'}), |
| 145 factory.TestData( |
116 label_en='devrec', | 146 label_en='devrec', |
| 147 label_zw='特殊模式', |
117 formal_name='factory_DeveloperRecovery', | 148 formal_name='factory_DeveloperRecovery', |
118 trigger='d', | 149 trigger='b', |
119 dargs={'layout':'devrec'}), | 150 dargs={'layout':'devrec'}), |
120 test_data( | 151 factory.TestData( |
121 label_en='run-in', | 152 label_en='final check', |
122 formal_name='step_runin', | 153 label_zw='最後檢查', |
123 automated_seq=[ | |
124 test_data( | |
125 label_en='component validation', | |
126 formal_name='hardware_Components', | |
127 dargs={'approved_db':'qualified_components'}), | |
128 test_data( | |
129 label_en='gpio switch check', | |
130 formal_name='hardware_GPIOSwitches'), | |
131 test_data( | |
132 label_en='system stress', | |
133 formal_name='hardware_SAT'), | |
134 test_data( | |
135 label_en='reboot (%s times)' % _REBOOT_SEQ_ITERATIONS, | |
136 formal_name='factory_RebootStub')], | |
137 trigger='r'), | |
138 test_data( | |
139 label_en='end', | |
140 formal_name='factory_Dummy', | 154 formal_name='factory_Dummy', |
141 trigger='e', | 155 trigger='f', |
142 dargs={'msg':'end of testing...\n(chinese)...'}), | 156 dargs={'msg':'google required checks...'}), |
| 157 factory.TestData( |
| 158 label_en='wipe', |
| 159 label_zw='擦拭', |
| 160 formal_name='factory_Dummy', |
| 161 trigger='x', |
| 162 dargs={'msg':('hit TAB+RETURN to finish testing and wipe test image!' + |
| 163 '...\n(chinese)...')}), |
143 ] | 164 ] |
144 | 165 |
145 for test in test_list: | 166 for test in test_list: |
146 test.tag_prefix = test.trigger | 167 test.tag_prefix = test.trigger |
147 for subtest in test.automated_seq: | 168 for subtest in test.automated_seq: |
148 subtest.tag_prefix = test.formal_name | 169 subtest.tag_prefix = test.formal_name |
149 | 170 |
150 def test_map_index(formal_name, tag_prefix): | 171 test_map = factory.make_test_map(test_list) |
151 return formal_name + '.' + tag_prefix | 172 trigger_set = factory.make_trigger_set(test_list) |
152 | |
153 test_map = dict((test_map_index(test.formal_name, test.tag_prefix), test) | |
154 for test in test_list) | |
155 | |
156 trigger_set = set(test.trigger for test in test_list) | |
157 | |
158 | |
159 class factory_ui: | |
160 '''Support communication with the factory_ui process. To simplify | |
161 surrounding code, this communication is an exchange of well formed | |
162 python expressions. Basically send wraps its arguments in a call | |
163 to repr() and recv calls eval() to re-generate the python data.''' | |
164 | |
165 def __init__(self, factory_ui_path): | |
166 self._proc = subprocess.Popen(factory_ui_path, | |
167 stdin=subprocess.PIPE, | |
168 stdout=subprocess.PIPE) | |
169 | |
170 def __del__(self): | |
171 XXX_log('control deleting factory_ui subprocess') | |
172 self._proc.terminate() | |
173 time.sleep(1) | |
174 if self._proc.poll() is None: | |
175 self._proc.kill() | |
176 | |
177 def send(self, x=None): | |
178 print >> self._proc.stdin, repr(x) | |
179 self._proc.stdin.flush() | |
180 | |
181 def send_cmd_next_test(self): | |
182 self.send(('next_test', None)) | |
183 | |
184 def send_cmd_switch_to(self, trigger): | |
185 self.send(('switch_to', trigger)) | |
186 | |
187 def recv(self): | |
188 return eval(self._proc.stdout.readline().rstrip()) | |
189 | |
190 def recv_target_test_update(self): | |
191 update = self.recv() | |
192 XXX_log('control recv target test %s' % repr(update)) | |
193 formal_name, tag_prefix, count = update | |
194 test = test_map.get(test_map_index(formal_name, tag_prefix), None) | |
195 return (test, count) | |
196 | 173 |
197 | 174 |
198 def step_reboot_seq(i, tag): | 175 def step_reboot_seq(i, tag): |
199 if i < _REBOOT_SEQ_ITERATIONS: | 176 if i < _REBOOT_SEQ_ITERATIONS: |
200 job.next_step_prepend([step_reboot_seq, i + 1, tag]) | 177 job.next_step_prepend([step_reboot_seq, i + 1, tag]) |
201 XXX_log('rebooting (iteration %d)' % i) | 178 factory.log('rebooting (iteration %d)' % i) |
202 time.sleep(5) | 179 time.sleep(5) |
203 job.reboot() | 180 job.reboot() |
204 else: | 181 else: |
205 job.run_test('factory_RebootStub', tag=tag) | 182 job.run_test('factory_RebootStub', tag=tag) |
206 step_init() | 183 step_init() |
207 | 184 |
208 | 185 |
209 def step_runin(ui, tag): | 186 def step_runin(ui, tag): |
210 job.run_test('hardware_Components', | 187 job.run_test('hardware_Components', |
211 approved_db='qualified_components', | 188 approved_db='qualified_components', |
(...skipping 12 matching lines...) Expand all Loading... |
224 out-of-order test execution based on keyboard shortcuts. | 201 out-of-order test execution based on keyboard shortcuts. |
225 | 202 |
226 For each test, a trigger (possibly None) is communicated to the | 203 For each test, a trigger (possibly None) is communicated to the |
227 UI, which then replies with the test name and a count number that | 204 UI, which then replies with the test name and a count number that |
228 becomes the autotest tag to allow repeated test execution while | 205 becomes the autotest tag to allow repeated test execution while |
229 preserving logs. | 206 preserving logs. |
230 | 207 |
231 When the tests themselves run, they are expected to look for | 208 When the tests themselves run, they are expected to look for |
232 (using the factory_test library) keyboard events that match test | 209 (using the factory_test library) keyboard events that match test |
233 switching triggers. When a trigger happens, it should be written | 210 switching triggers. When a trigger happens, it should be written |
234 to the _RESULT_FILE_PATH, which will be read after the test | 211 to the factory.RESULT_FILE_PATH, which will be read after the test |
235 completed and the result comminicated onwards to the UI.''' | 212 completed and the result comminicated onwards to the UI.''' |
236 | 213 |
237 job.next_step([step_init]) | 214 job.next_step([step_init]) |
238 | 215 |
239 status_file_path = job.autodir + '/results/default/status' | 216 ui = factory.UiClient(_FACTORY_UI_PATH) |
240 factory_ui_path = job.autodir + '/deps/factory/ui' | |
241 | 217 |
242 ui = factory_ui(factory_ui_path) | |
243 | |
244 ui.send(test_data_class_def) | |
245 ui.send(test_list) | 218 ui.send(test_list) |
246 ui.send(status_file_path) | 219 ui.send(_STATUS_FILE_PATH) |
247 ui.send(_FACTORY_LOG_PATH) | |
248 | 220 |
249 test_widget_size = ui.recv() | 221 test_widget_size = ui.recv() |
250 XXX_log('received test_widget_size = %s' % repr(test_widget_size)) | 222 factory.log('received test_widget_size = %s' % repr(test_widget_size)) |
251 | 223 |
252 ui.send_cmd_next_test() | 224 ui.send_cmd_next_test() |
253 test, test_count = ui.recv_target_test_update() | 225 test, test_count = ui.recv_target_test_update(test_map) |
254 | 226 |
255 while test is not None: | 227 while test is not None: |
256 if test.automated_seq: | 228 if test.automated_seq: |
257 tag = '%s_%s' % (test.formal_name, test_count) | 229 tag = '%s_%s' % (test.formal_name, test_count) |
258 exec('%s(ui, "%s")' % (test.formal_name, tag)) | 230 exec('%s(ui, "%s")' % (test.formal_name, tag)) |
259 result = None | 231 result = None |
260 else: | 232 else: |
261 dargs = test.dargs | 233 dargs = test.dargs |
262 dargs.update({ | 234 dargs.update({ |
263 'tag': '%s_%s' % (test.tag_prefix, test_count), | 235 'tag': '%s_%s' % (test.tag_prefix, test_count), |
264 'test_tag_prefix': test.tag_prefix, | 236 'test_tag_prefix': test.tag_prefix, |
265 'test_count': test_count, | 237 'test_count': test_count, |
266 'test_widget_size': test_widget_size, | 238 'test_widget_size': test_widget_size, |
267 'trigger_set': trigger_set, | 239 'trigger_set': trigger_set, |
268 'result_file_path': _RESULT_FILE_PATH}) | 240 'result_file_path': factory.RESULT_FILE_PATH}) |
269 with open(_RESULT_FILE_PATH, 'w') as file: | 241 with open(factory.RESULT_FILE_PATH, 'w') as file: |
270 file.write('None\n') | 242 file.write('None\n') |
271 job.run_test(test.formal_name, **dargs) | 243 job.run_test(test.formal_name, **dargs) |
272 with open(_RESULT_FILE_PATH, 'r') as file: | 244 with open(factory.RESULT_FILE_PATH, 'r') as file: |
273 result = eval(file.readline()) | 245 result = eval(file.readline()) |
274 | 246 |
275 if result is not None: | 247 if result is not None: |
276 ui.send_cmd_switch_to(result) | 248 ui.send_cmd_switch_to(result) |
277 else: | 249 else: |
278 ui.send_cmd_next_test() | 250 ui.send_cmd_next_test() |
279 | 251 |
280 test, test_count = ui.recv_target_test_update() | 252 test, test_count = ui.recv_target_test_update(test_map) |
281 | 253 |
282 XXX_log('factory testing completed') | 254 factory.log('factory testing completed') |
OLD | NEW |