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_Camera', | 76 factory.TestData( |
102 trigger='c'), | 77 label_en='system stress', |
103 test_data( | 78 label_zw='壓力測試', |
| 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( |
104 label_en='keyboard', | 86 label_en='keyboard', |
105 label_zw='鍵盤', | 87 label_zw='鍵盤', |
106 formal_name='factory_Keyboard', | 88 formal_name='factory_Keyboard', |
107 trigger='k', | 89 trigger='k', |
108 dargs={'layout':'en_us'}), | 90 dargs={'layout':'en_us'}), |
109 test_data( | 91 factory.TestData( |
110 label_en='touchpad', | 92 label_en='touchpad', |
111 label_zw='觸控板', | 93 label_zw='觸控板', |
112 formal_name='factory_Touchpad', | 94 formal_name='factory_Touchpad', |
113 trigger='t'), | 95 trigger='t'), |
114 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_Camera', |
| 111 trigger='c'), |
| 112 factory.TestData( |
| 113 label_en='audio', |
| 114 label_zw='聲音', |
| 115 formal_name='factory_Dummy', |
| 116 trigger='a', |
| 117 dargs={'msg':'audio test, one day...'}), |
| 118 factory.TestData( |
| 119 label_en='usb', |
| 120 formal_name='factory_ExternalStorage', |
| 121 trigger='u'), |
| 122 factory.TestData( |
| 123 label_en='sd', |
| 124 formal_name='factory_ExternalStorage', |
| 125 trigger='d'), |
| 126 factory.TestData( |
| 127 label_en='bluetooth', |
| 128 label_zw='藍牙', |
| 129 formal_name='factory_Dummy', |
| 130 trigger='o', |
| 131 dargs={'msg':'bluetooth test, one day...'}), |
| 132 factory.TestData( |
| 133 label_en='3g', |
| 134 label_zw='第三代', |
| 135 formal_name='factory_Dummy', |
| 136 trigger='g', |
| 137 dargs={'msg':'3g test, one day...'}), |
| 138 factory.TestData( |
| 139 label_en='wifi', |
| 140 label_zw='無線上網', |
| 141 formal_name='factory_Dummy', |
| 142 trigger='w', |
| 143 dargs={'msg':'wifi test, one day...'}), |
| 144 factory.TestData( |
115 label_en='devrec', | 145 label_en='devrec', |
| 146 label_zw='特殊模式', |
116 formal_name='factory_DeveloperRecovery', | 147 formal_name='factory_DeveloperRecovery', |
117 trigger='d', | 148 trigger='b', |
118 dargs={'layout':'devrec'}), | 149 dargs={'layout':'devrec'}), |
119 test_data( | 150 factory.TestData( |
120 label_en='run-in', | 151 label_en='final check', |
121 formal_name='step_runin', | 152 label_zw='最後檢查', |
122 automated_seq=[ | |
123 test_data( | |
124 label_en='component validation', | |
125 formal_name='hardware_Components', | |
126 dargs={'approved_db':'qualified_components'}), | |
127 test_data( | |
128 label_en='gpio switch check', | |
129 formal_name='hardware_GPIOSwitches'), | |
130 test_data( | |
131 label_en='system stress', | |
132 formal_name='hardware_SAT'), | |
133 test_data( | |
134 label_en='reboot (%s times)' % _REBOOT_SEQ_ITERATIONS, | |
135 formal_name='factory_RebootStub')], | |
136 trigger='r'), | |
137 test_data( | |
138 label_en='end', | |
139 formal_name='factory_Dummy', | 153 formal_name='factory_Dummy', |
140 trigger='e', | 154 trigger='f', |
141 dargs={'msg':'end of testing...\n(chinese)...'}), | 155 dargs={'msg':'google required checks...'}), |
| 156 factory.TestData( |
| 157 label_en='wipe', |
| 158 label_zw='擦拭', |
| 159 formal_name='factory_Dummy', |
| 160 trigger='x', |
| 161 dargs={'msg':('hit TAB+RETURN to finish testing and wipe test image!' + |
| 162 '...\n(chinese)...')}), |
142 ] | 163 ] |
143 | 164 |
144 for test in test_list: | 165 for test in test_list: |
145 test.tag_prefix = test.trigger | 166 test.tag_prefix = test.trigger |
146 for subtest in test.automated_seq: | 167 for subtest in test.automated_seq: |
147 subtest.tag_prefix = test.formal_name | 168 subtest.tag_prefix = test.formal_name |
148 | 169 |
149 def test_map_index(formal_name, tag_prefix): | 170 test_map = factory.make_test_map(test_list) |
150 return formal_name + '.' + tag_prefix | 171 trigger_set = factory.make_trigger_set(test_list) |
151 | |
152 test_map = dict((test_map_index(test.formal_name, test.tag_prefix), test) | |
153 for test in test_list) | |
154 | |
155 trigger_set = set(test.trigger for test in test_list) | |
156 | |
157 | |
158 class factory_ui: | |
159 '''Support communication with the factory_ui process. To simplify | |
160 surrounding code, this communication is an exchange of well formed | |
161 python expressions. Basically send wraps its arguments in a call | |
162 to repr() and recv calls eval() to re-generate the python data.''' | |
163 | |
164 def __init__(self, factory_ui_path): | |
165 self._proc = subprocess.Popen(factory_ui_path, | |
166 stdin=subprocess.PIPE, | |
167 stdout=subprocess.PIPE) | |
168 | |
169 def __del__(self): | |
170 XXX_log('control deleting factory_ui subprocess') | |
171 self._proc.terminate() | |
172 time.sleep(1) | |
173 if self._proc.poll() is None: | |
174 self._proc.kill() | |
175 | |
176 def send(self, x=None): | |
177 print >> self._proc.stdin, repr(x) | |
178 self._proc.stdin.flush() | |
179 | |
180 def send_cmd_next_test(self): | |
181 self.send(('next_test', None)) | |
182 | |
183 def send_cmd_switch_to(self, trigger): | |
184 self.send(('switch_to', trigger)) | |
185 | |
186 def recv(self): | |
187 return eval(self._proc.stdout.readline().rstrip()) | |
188 | |
189 def recv_target_test_update(self): | |
190 update = self.recv() | |
191 XXX_log('control recv target test %s' % repr(update)) | |
192 formal_name, tag_prefix, count = update | |
193 test = test_map.get(test_map_index(formal_name, tag_prefix), None) | |
194 return (test, count) | |
195 | 172 |
196 | 173 |
197 def step_reboot_seq(i, tag): | 174 def step_reboot_seq(i, tag): |
198 if i < _REBOOT_SEQ_ITERATIONS: | 175 if i < _REBOOT_SEQ_ITERATIONS: |
199 job.next_step_prepend([step_reboot_seq, i + 1, tag]) | 176 job.next_step_prepend([step_reboot_seq, i + 1, tag]) |
200 XXX_log('rebooting (iteration %d)' % i) | 177 factory.log('rebooting (iteration %d)' % i) |
201 time.sleep(5) | 178 time.sleep(5) |
202 job.reboot() | 179 job.reboot() |
203 else: | 180 else: |
204 job.run_test('factory_RebootStub', tag=tag) | 181 job.run_test('factory_RebootStub', tag=tag) |
205 step_init() | 182 step_init() |
206 | 183 |
207 | 184 |
208 def step_runin(ui, tag): | 185 def step_runin(ui, tag): |
209 job.run_test('hardware_Components', | 186 job.run_test('hardware_Components', |
210 approved_db='qualified_components', | 187 approved_db='qualified_components', |
(...skipping 12 matching lines...) Expand all Loading... |
223 out-of-order test execution based on keyboard shortcuts. | 200 out-of-order test execution based on keyboard shortcuts. |
224 | 201 |
225 For each test, a trigger (possibly None) is communicated to the | 202 For each test, a trigger (possibly None) is communicated to the |
226 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 |
227 becomes the autotest tag to allow repeated test execution while | 204 becomes the autotest tag to allow repeated test execution while |
228 preserving logs. | 205 preserving logs. |
229 | 206 |
230 When the tests themselves run, they are expected to look for | 207 When the tests themselves run, they are expected to look for |
231 (using the factory_test library) keyboard events that match test | 208 (using the factory_test library) keyboard events that match test |
232 switching triggers. When a trigger happens, it should be written | 209 switching triggers. When a trigger happens, it should be written |
233 to the _RESULT_FILE_PATH, which will be read after the test | 210 to the factory.RESULT_FILE_PATH, which will be read after the test |
234 completed and the result comminicated onwards to the UI.''' | 211 completed and the result comminicated onwards to the UI.''' |
235 | 212 |
236 job.next_step([step_init]) | 213 job.next_step([step_init]) |
237 | 214 |
238 status_file_path = job.autodir + '/results/default/status' | 215 ui = factory.UiClient(_FACTORY_UI_PATH) |
239 factory_ui_path = job.autodir + '/deps/factory/ui' | |
240 | 216 |
241 ui = factory_ui(factory_ui_path) | |
242 | |
243 ui.send(test_data_class_def) | |
244 ui.send(test_list) | 217 ui.send(test_list) |
245 ui.send(status_file_path) | 218 ui.send(_STATUS_FILE_PATH) |
246 ui.send(_FACTORY_LOG_PATH) | |
247 | 219 |
248 test_widget_size = ui.recv() | 220 test_widget_size = ui.recv() |
249 XXX_log('received test_widget_size = %s' % repr(test_widget_size)) | 221 factory.log('received test_widget_size = %s' % repr(test_widget_size)) |
250 | 222 |
251 ui.send_cmd_next_test() | 223 ui.send_cmd_next_test() |
252 test, test_count = ui.recv_target_test_update() | 224 test, test_count = ui.recv_target_test_update(test_map) |
253 | 225 |
254 while test is not None: | 226 while test is not None: |
255 if test.automated_seq: | 227 if test.automated_seq: |
256 tag = '%s_%s' % (test.formal_name, test_count) | 228 tag = '%s_%s' % (test.formal_name, test_count) |
257 exec('%s(ui, "%s")' % (test.formal_name, tag)) | 229 exec('%s(ui, "%s")' % (test.formal_name, tag)) |
258 result = None | 230 result = None |
259 else: | 231 else: |
260 dargs = test.dargs | 232 dargs = test.dargs |
261 dargs.update({ | 233 dargs.update({ |
262 'tag': '%s_%s' % (test.tag_prefix, test_count), | 234 'tag': '%s_%s' % (test.tag_prefix, test_count), |
263 'test_tag_prefix': test.tag_prefix, | 235 'test_tag_prefix': test.tag_prefix, |
264 'test_count': test_count, | 236 'test_count': test_count, |
265 'test_widget_size': test_widget_size, | 237 'test_widget_size': test_widget_size, |
266 'trigger_set': trigger_set, | 238 'trigger_set': trigger_set, |
267 'result_file_path': _RESULT_FILE_PATH}) | 239 'result_file_path': factory.RESULT_FILE_PATH}) |
268 with open(_RESULT_FILE_PATH, 'w') as file: | 240 with open(factory.RESULT_FILE_PATH, 'w') as file: |
269 file.write('None\n') | 241 file.write('None\n') |
270 job.run_test(test.formal_name, **dargs) | 242 job.run_test(test.formal_name, **dargs) |
271 with open(_RESULT_FILE_PATH, 'r') as file: | 243 with open(factory.RESULT_FILE_PATH, 'r') as file: |
272 result = eval(file.readline()) | 244 result = eval(file.readline()) |
273 | 245 |
274 if result is not None: | 246 if result is not None: |
275 ui.send_cmd_switch_to(result) | 247 ui.send_cmd_switch_to(result) |
276 else: | 248 else: |
277 ui.send_cmd_next_test() | 249 ui.send_cmd_next_test() |
278 | 250 |
279 test, test_count = ui.recv_target_test_update() | 251 test, test_count = ui.recv_target_test_update(test_map) |
280 | 252 |
281 XXX_log('factory testing completed') | 253 factory.log('factory testing completed') |
OLD | NEW |