| 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 |