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 imp |
27 import subprocess | |
28 import sys | |
29 import time | |
30 | |
31 imp.load_source('common', job.autodir + '/bin/common.py') | 27 imp.load_source('common', job.autodir + '/bin/common.py') |
32 from autotest_lib.client.bin import factory | 28 from autotest_lib.client.bin import factory |
33 from autotest_lib.client.bin import utils | 29 from autotest_lib.client.bin import utils |
34 | 30 |
35 | 31 |
36 # -- CUSTOMIZATION -------------------------------------------------- | 32 # These definitions are expose these classes directly into this |
37 # You can tune these settings to fit your factory process. | 33 # namespace, so that the following exec'ed file can have cleaner |
38 | 34 # syntax. These are done in this fashion, as opposed to "from factory |
39 # change this to False if you want and allow fast wipe (insecure) | 35 # import <class>" to work-around Python namespace wackiness -- the |
40 DO_FACTORY_SECURE_WIPE = True | 36 # from syntax does not work, creating new classes. |
41 | 37 OperatorTest = factory.OperatorTest |
42 # -- END OF CUSTOMIZATION ------------------------------------------ | 38 InformationScreen = factory.InformationScreen |
| 39 AutomatedSequence = factory.AutomatedSequence |
| 40 AutomatedSubTest = factory.AutomatedSubTest |
| 41 AutomatedRebootSubTest = factory.AutomatedRebootSubTest |
43 | 42 |
44 | 43 |
45 _RESULTS_PATH = job.autodir + '/results/default' | 44 # This exec defines TEST_LIST in global scope. |
46 _STATUS_FILE_PATH = _RESULTS_PATH + '/status' | 45 execfile(job.autodir + '/site_tests/suite_Factory/test_list') |
47 _FACTORY_UI_PATH = job.autodir + '/bin/factory_ui' | |
48 | |
49 | |
50 _REBOOT_SEQ_ITERATIONS = 2 | |
51 | 46 |
52 | 47 |
53 # Hack to work around autotest's obsession with GRUB. | 48 # Hack to work around autotest's obsession with GRUB. |
54 job.bootloader.set_default = lambda x: None | 49 job.bootloader.set_default = lambda x: None |
55 job.bootloader.boot_once = lambda x: None | 50 job.bootloader.boot_once = lambda x: None |
56 | 51 |
57 | 52 |
58 # TEST ORDERING: Tests in the test_list will be run in the order | 53 def step_reboot_seq(tag_prefix, total_iterations, i=0): |
59 # below, unless the operator interrupts the flow via keyboard | 54 if i < total_iterations: |
60 # shortcut. To cause immediate execution of the run-in tests, for | 55 job.next_step_prepend([step_reboot_seq, tag_prefix, |
61 # example, reorder runin to occur as the first test in the test_list. | 56 total_iterations, i + 1]) |
62 | 57 factory.log('rebooting (iteration %d of %d)' % (i, total_iterations)) |
63 test_list = [ | 58 job.reboot() |
64 factory.TestData( | 59 else: |
65 label_en='start', | 60 step_init(intentional_reboot_subtest_tag_prefix=tag_prefix) |
66 label_zw='開始', | |
67 formal_name='factory_Dummy', | |
68 trigger='e', | |
69 dargs={'quit_key':ord(' '), | |
70 'msg':'Hit SPACE to start testing...\n按 "空白鍵" 開始測試...'}), | |
71 factory.TestData( | |
72 label_en='sync', | |
73 label_zw='同步', | |
74 formal_name='factory_ScriptWrapper', | |
75 trigger='s', | |
76 dargs={'cmdline': job.autodir + | |
77 '/site_tests/factory_ScriptWrapper/dummy.sh'}), | |
78 factory.TestData( | |
79 label_en='run-in', | |
80 label_zw='燒機測試', | |
81 formal_name='step_runin', | |
82 automated_seq=[ | |
83 # Match HWQual ID by running hardware_Components with ignored cids. | |
84 factory.TestData( | |
85 label_en='hwqual id matching', | |
86 label_zw='型號匹配', | |
87 formal_name='hardware_Components', | |
88 dargs={'approved_dbs':'qualified_components*', | |
89 'ignored_cids':[ | |
90 'hash_ro_firmware', | |
91 'part_id_hwqual', | |
92 'vendor_id_bios', | |
93 'version_rw_firmware', | |
94 ]}), | |
95 factory.TestData( | |
96 label_en='gpio switch check', | |
97 label_zw='檢查 gpio 開關', | |
98 formal_name='hardware_GPIOSwitches'), | |
99 factory.TestData( | |
100 label_en='system stress', | |
101 label_zw='壓力測試', | |
102 formal_name='hardware_SAT', | |
103 dargs={'seconds': 60}), | |
104 factory.TestData( | |
105 label_en='graphics', | |
106 label_zw='圖型', | |
107 formal_name='graphics_GLBench'), | |
108 factory.TestData( | |
109 label_en='reboot (%s times)' % _REBOOT_SEQ_ITERATIONS, | |
110 label_zw='重新開機 (%s 次)' % _REBOOT_SEQ_ITERATIONS, | |
111 formal_name='factory_RebootStub')], | |
112 trigger='r'), | |
113 factory.TestData( | |
114 label_en='keyboard', | |
115 label_zw='鍵盤', | |
116 formal_name='factory_Keyboard', | |
117 trigger='k', | |
118 dargs={'layout':'en_us'}), | |
119 factory.TestData( | |
120 label_en='touchpad', | |
121 label_zw='觸控板', | |
122 formal_name='factory_Touchpad', | |
123 trigger='t'), | |
124 factory.TestData( | |
125 label_en='leds', | |
126 label_zw='機身側燈', | |
127 formal_name='factory_Leds', | |
128 trigger='l', | |
129 dargs={'led_ctl_path': | |
130 '/usr/local/autotest/site_tests/factory_Leds/src/ec_ctl'}), | |
131 factory.TestData( | |
132 label_en='display', | |
133 label_zw='顯示', | |
134 formal_name='factory_Display', | |
135 trigger='m'), | |
136 factory.TestData( | |
137 label_en='ExtDisplay', | |
138 label_zw='外接顯示', | |
139 formal_name='factory_ExtDisplay', | |
140 trigger='n', | |
141 dargs={'has_audio':True, | |
142 'sample':'deps/factory/fhorn.wav'}), | |
143 factory.TestData( | |
144 label_en='camera', | |
145 label_zw='相機', | |
146 formal_name='factory_Camera', | |
147 trigger='c'), | |
148 factory.TestData( | |
149 label_en='audio', | |
150 label_zw='音源裝置', | |
151 formal_name='factory_Audio', | |
152 trigger='a', | |
153 dargs={'sample':'deps/factory/fhorn.wav'}), | |
154 factory.TestData( | |
155 label_en='usb', | |
156 formal_name='factory_ExternalStorage', | |
157 trigger='u', | |
158 dargs={'media':'USB'}), | |
159 factory.TestData( | |
160 label_en='sd', | |
161 formal_name='factory_ExternalStorage', | |
162 trigger='d', | |
163 dargs={'media':'SD'}), | |
164 factory.TestData( | |
165 label_en='bluetooth', | |
166 label_zw='藍芽', | |
167 formal_name='factory_Dummy', | |
168 trigger='o', | |
169 dargs={'msg':'bluetooth test, one day...'}), | |
170 factory.TestData( | |
171 label_en='3g', | |
172 label_zw='3G上網', | |
173 formal_name='factory_Dummy', | |
174 trigger='g', | |
175 dargs={'msg':'3g test, one day...'}), | |
176 factory.TestData( | |
177 label_en='wifi', | |
178 label_zw='無線上網', | |
179 formal_name='factory_Dummy', | |
180 trigger='w', | |
181 dargs={'msg':'wifi test, one day...'}), | |
182 factory.TestData( | |
183 label_en='light sensor', | |
184 label_zw='光传感器', | |
185 formal_name='factory_LightSensor', | |
186 dargs={'lux_max':1000, 'lux_min':1}, | |
187 trigger='i'), | |
188 | |
189 # THIS IS A GOOGLE REQUIRED TEST. | |
190 # PLEASE DO NOT REMOVE THIS TEST IN PRODUCTION RELEASES. | |
191 factory.TestData( | |
192 label_en='devrec', | |
193 label_zw='還原模式', | |
194 formal_name='factory_DeveloperRecovery', | |
195 trigger='b', | |
196 dargs={'layout':'devrec'}), | |
197 | |
198 # THIS IS A GOOGLE REQUIRED TEST. | |
199 # PLEASE DO NOT REMOVE THIS TEST IN PRODUCTION RELEASES. | |
200 factory.TestData( | |
201 label_en='final', | |
202 label_zw='最後測試', | |
203 formal_name='step_final_stage1', | |
204 automated_seq=[ | |
205 # THIS IS A GOOGLE REQUIRED TEST. | |
206 # PLEASE DO NOT REMOVE THIS TEST IN PRODUCTION RELEASES. | |
207 factory.TestData( | |
208 label_en='write GBB', | |
209 label_zw='寫入GBB', | |
210 formal_name='factory_WriteGBB'), | |
211 factory.TestData( | |
212 label_en='reboot', | |
213 label_zw='重新開機', | |
214 formal_name='factory_RebootStub'), | |
215 factory.TestData( | |
216 label_en='component validation', | |
217 label_zw='元件驗證', | |
218 formal_name='hardware_Components')], | |
219 trigger='f'), | |
220 | |
221 # THIS IS A GOOGLE REQUIRED TEST. | |
222 # PLEASE DO NOT REMOVE THIS TEST IN PRODUCTION DEVICES. | |
223 factory.TestData( | |
224 label_en='wipe', | |
225 label_zw='清除', | |
226 formal_name='factory_Wipe', | |
227 trigger='x', | |
228 dargs={'secure_wipe':DO_FACTORY_SECURE_WIPE, | |
229 'write_protect':False, | |
230 'msg':('hit TAB+ENTER to write protect FW and ' | |
231 'wipe test image!...\n' | |
232 '請按下 TAB+ENTER 啟用寫入保護以及清除' | |
233 '測試程式資料!...')}), | |
234 # NOTE: factory_Wipe.dargs support following variables for temporary | |
235 # testing. You can add these settings to help internal test, but | |
236 # THESE ARE GOOGLE REQUIRED TESTS SO YOU SHOULD NOT DISABLE THEM | |
237 # IN PRODUCTION RELEASES. | |
238 # - check_developer_switch: check for developer button switch | |
239 # - write_protect: enable and check flash ROM write protection | |
240 # TODO(hungte) Although the 'write_protect' should be a Google required | |
241 # test, we decide to disable it until first official release of | |
242 # Chrome OS. Please remove that line for the real release. | |
243 | |
244 factory.TestData( | |
245 label_en='review', | |
246 label_zw='報告', | |
247 formal_name='factory_Review', | |
248 repeat_forever=True, | |
249 trigger='z'), | |
250 ] | |
251 | |
252 for test in test_list: | |
253 test.tag_prefix = test.trigger | |
254 for subtest in test.automated_seq: | |
255 subtest.tag_prefix = test.formal_name | |
256 | |
257 test_map = factory.make_test_map(test_list) | |
258 trigger_set = factory.make_trigger_set(test_list) | |
259 | 61 |
260 | 62 |
261 def run_subtest(name, dargs_map): | 63 def step_init(intentional_reboot_subtest_tag_prefix=None): |
262 job.run_test(name, **dargs_map[name]) | |
263 | |
264 | |
265 def step_reboot_seq(dargs_map, i=0): | |
266 if i < _REBOOT_SEQ_ITERATIONS: | |
267 job.next_step_prepend([step_reboot_seq, dargs_map, i + 1]) | |
268 factory.log('rebooting (iteration %d)' % i) | |
269 time.sleep(5) | |
270 job.reboot() | |
271 else: | |
272 run_subtest('factory_RebootStub', dargs_map) | |
273 step_init() | |
274 | |
275 | |
276 hwqual_id = None | |
277 | |
278 def get_hwqual_id(): | |
279 # TODO: Move HWQual ID to StatusMap. | |
280 global hwqual_id | |
281 if not hwqual_id: | |
282 hwqual_id = find_hwqual_id() | |
283 return hwqual_id | |
284 | |
285 def find_hwqual_id(): | |
286 cmd = ('find %s -name keyval | xargs grep -h ^hwqual_id= | ' | |
287 'sed s/hwqual_id=// | head -1' % | |
288 (_RESULTS_PATH + '/hardware_Components.step_runin_*')) | |
289 hwqual_id = utils.system_output(cmd).strip() | |
290 hwqual_id = hwqual_id.rsplit(' ', 1)[0].replace(' ', '_') | |
291 if not hwqual_id: | |
292 factory.log('fail to get the HWQual ID') | |
293 return hwqual_id | |
294 | |
295 | |
296 def update_dargs(test, dargs_map, update_map): | |
297 for (key, value) in update_map.iteritems(): | |
298 dargs_map[test][key] = value | |
299 | |
300 | |
301 def step_runin(ui, dargs_map): | |
302 run_subtest('hardware_Components', dargs_map) | |
303 run_subtest('hardware_GPIOSwitches', dargs_map) | |
304 job.drop_caches_between_iterations = True | |
305 run_subtest('hardware_SAT', dargs_map) | |
306 job.drop_caches_between_iterations = False | |
307 run_subtest('graphics_GLBench', dargs_map) | |
308 step_reboot_seq(dargs_map) | |
309 | |
310 | |
311 def step_final_stage1(ui, dargs_map): | |
312 update_dargs('factory_WriteGBB', dargs_map, | |
313 {'gbb_file': 'gbb_*%s' % get_hwqual_id()}) | |
314 run_subtest('factory_WriteGBB', dargs_map) | |
315 job.next_step_prepend([step_final_stage2, ui, dargs_map]) | |
316 factory.log('rebooting') | |
317 time.sleep(5) | |
318 job.reboot() | |
319 | |
320 | |
321 def step_final_stage2(ui, dargs_map): | |
322 run_subtest('factory_RebootStub', dargs_map) | |
323 update_dargs('hardware_Components', dargs_map, | |
324 {'approved_dbs': 'qualified_components_*%s' % get_hwqual_id()}) | |
325 run_subtest('hardware_Components', dargs_map) | |
326 step_init() | |
327 | |
328 | |
329 def step_init(): | |
330 '''Launch the factory UI, which will then make decisions on which | |
331 tests to run in which order. This is to support user driven | |
332 out-of-order test execution based on keyboard shortcuts. | |
333 | |
334 For each test, a trigger (possibly None) is communicated to the | |
335 UI, which then replies with the test name and a count number that | |
336 becomes the autotest tag to allow repeated test execution while | |
337 preserving logs. | |
338 | |
339 When the tests themselves run, they are expected to look for | |
340 (using the factory_test library) keyboard events that match test | |
341 switching triggers. When a trigger happens, it should be written | |
342 to the factory.RESULT_FILE_PATH, which will be read after the test | |
343 completed and the result comminicated onwards to the UI.''' | |
344 | |
345 job.next_step([step_init]) | 64 job.next_step([step_init]) |
346 | 65 |
347 ui = factory.UiClient(_FACTORY_UI_PATH) | 66 factory_ui_path = job.autodir + '/bin/factory_ui' |
| 67 status_file_path = job.autodir + '/results/default/status' |
348 | 68 |
349 ui.send(test_list) | 69 status_map = factory.StatusMap(TEST_LIST, status_file_path) |
350 ui.send(_STATUS_FILE_PATH) | 70 ui = factory.UiClient(TEST_LIST, factory_ui_path, status_file_path) |
| 71 control_state = factory.ControlState(job, TEST_LIST, ui, status_map, |
| 72 status_file_path) |
351 | 73 |
352 test_widget_size = ui.recv() | 74 if intentional_reboot_subtest_tag_prefix: |
353 factory.log('received test_widget_size = %s' % repr(test_widget_size)) | 75 reboot_subtest = status_map.test_db.get_subtest_by_tag_prefix( |
| 76 intentional_reboot_subtest_tag_prefix) |
| 77 control_state.run_test(reboot_subtest) |
| 78 status_map.read_new_data() |
354 | 79 |
355 ui.send_cmd_next_test() | 80 test = status_map.next_untested() |
356 test, test_count = ui.recv_target_test_update(test_map) | |
357 | |
358 while test is not None: | 81 while test is not None: |
359 if test.automated_seq: | 82 if isinstance(test, factory.AutomatedSequence): |
360 tag = '%s_%s' % (test.formal_name, test_count) | 83 for subtest in test.subtest_list: |
361 dargs_map = dict((st.formal_name, st.dargs) | 84 if isinstance(subtest, factory.AutomatedRebootSubTest): |
362 for st in test.automated_seq) | 85 tag_prefix = status_map.test_db.get_tag_prefix(subtest) |
363 for i in dargs_map: | 86 step_reboot_seq(tag_prefix, subtest.iterations) |
364 dargs_map[i].update(tag=tag) | 87 else: |
365 exec('%s(ui, dargs_map)' % (test.formal_name)) | 88 control_state.run_test(subtest) |
366 result = None | 89 if control_state.activated_kbd_shortcut_test: |
| 90 break |
367 else: | 91 else: |
368 dargs = test.dargs | 92 control_state.run_test(test) |
369 dargs.update({ | 93 status_map.read_new_data() |
370 'tag': '%s_%s' % (test.tag_prefix, test_count), | 94 test = (control_state.activated_kbd_shortcut_test or |
371 'test_tag_prefix': test.tag_prefix, | 95 status_map.next_untested()) |
372 'test_count': test_count, | |
373 'test_widget_size': test_widget_size, | |
374 'trigger_set': trigger_set, | |
375 'status_file_path' : _STATUS_FILE_PATH, | |
376 'test_list': test_list}) | |
377 with open(factory.RESULT_FILE_PATH, 'w') as file: | |
378 file.write('None\n') | |
379 job.run_test(test.formal_name, **dargs) | |
380 with open(factory.RESULT_FILE_PATH, 'r') as file: | |
381 result = eval(file.readline()) | |
382 | |
383 if result is not None: | |
384 ui.send_cmd_switch_to(result) | |
385 else: | |
386 ui.send_cmd_next_test() | |
387 | |
388 test, test_count = ui.recv_target_test_update(test_map) | |
389 | |
390 factory.log('factory testing completed') | |
OLD | NEW |