3 # Copyright (C) 2012 Intel Corporation
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
16 # Jing,Wang <jing.j.wang@intel.com>
17 # Yuanyuan,Zou <yuanyuanx.zou@intel.com>
18 """ testkit lite tools"""
26 import xml.etree.ElementTree as etree
27 from optparse import OptionParser, make_option
28 from datetime import datetime
34 from testkitlite.util.log import LOGGER
35 except ImportError, err:
36 print "[ Error: loading logging failed, error: %s ]\n" % err
37 print "try to run command " \
38 "'export PYTHONPATH=/usr/local/lib/python2.7/dist-packages' or " \
39 "'export PYTHONPATH=/usr/local/lib/python2.7/site-packages' to resolve module missed issue"
43 # get platform version info
44 OS_VER = platform.system()
46 EXISTS = os.path.exists
47 DIRNAME = os.path.dirname
48 BASENAME = os.path.basename
49 ABSPATH = os.path.abspath
51 ISLINK = os.path.islink
53 TESTKIT_DIR = "/opt/testkit/lite"
54 if not OS_VER == "Linux" and not OS_VER == "Darwin":
55 TESTKIT_DIR = DIRNAME(ABSPATH(__file__))
56 sys.path += [JOIN(TESTKIT_DIR)]
57 TESTKIT_DIR = JOIN(TESTKIT_DIR, "results")
60 TEST_PACKAGES_DIR = JOIN(TESTKIT_DIR, "test_packages")
71 can_merge_result = False
76 # detect version option
77 if "--version" in sys.argv:
79 CONFIG = ConfigParser.ConfigParser()
80 if platform.system() == "Linux":
81 CONFIG.read('/opt/testkit/lite/VERSION')
83 VERSION_FILE = JOIN(sys.path[0], 'VERSION')
84 CONFIG.read(VERSION_FILE)
85 VERSION = CONFIG.get('public_version', 'version')
86 LOGGER.info("V%s" % VERSION)
88 except ConfigParser.Error, err:
90 "[ Error: fail to parse version info, error: %s ]\n" % err)
93 # detect internal version option
94 if "--internal-version" in sys.argv:
96 CONFIG = ConfigParser.ConfigParser()
97 if platform.system() == "Linux":
98 CONFIG.read('/opt/testkit/lite/VERSION')
100 VERSION_FILE = JOIN(sys.path[0], 'VERSION')
101 CONFIG.read(VERSION_FILE)
102 VERSION = CONFIG.get('internal_version', 'version')
105 except ConfigParser.Error, err:
106 print "[ Error: fail to parse version info, error: %s ]\n" % err
110 def varnarg(option, opt_str, value, parser):
114 for arg in parser.rargs:
115 if re.search('^--.+', arg) or \
116 re.search('^-[\D]', arg):
120 del parser.rargs[:len(value)]
121 setattr(parser.values, option.dest, value)
123 def unlock_and_exit(exit_code=signal.SIGINT):
125 release_device_lock(device_id)
128 def final_clean_test():
130 if RUNNER is not None:
131 if RUNNER.session_id:
132 RUNNER.finalize_test(RUNNER.session_id)
134 RUNNER.merge_resultfile(START_TIME, CURRENT_LOG_DIR)
136 clean_testxml(OPTIONS.testxml, remote_test)
137 except (KeyboardInterrupt, Exception), err:
140 def sig_exit_handler(*args):
142 LOGGER.info("\n[ exiting testkit-lite on system signal ]\n")
145 signal.signal(signal.SIGTSTP, sig_exit_handler)
146 signal.signal(signal.SIGTERM, sig_exit_handler)
149 make_option("-f", "--testxml", dest="testxml",
150 action="callback", callback=varnarg,
151 help="Specify the path of test definition file (tests.xml)."
152 " If run more one test package,just list the all the path "
153 " of \"tests.xml\" and separate with a whitespace"),
154 make_option("-D", "--dryrun", dest="bdryrun",
156 help="Dry-run the selected test cases"),
157 make_option("-M", "--manual-only", dest="bmanualonly",
159 help="Enable only manual tests"),
160 make_option("-A", "--auto-only", dest="bautoonly",
162 help="Enable only auto tests"),
163 make_option("-o", "--output", dest="resultfile",
164 help="Specify output file for result xml. \
165 If more than one testxml provided, \
166 results will be merged together to this output file"),
167 make_option("-e", dest="exttest", action="store",
168 help="Launch external test with a launcher,\
169 supports browser or other web-runtime"),
170 make_option("-k", "--worker", dest="worker", action="store",
171 help="Specify a test engine for execution, use value 'default' by default"),
172 make_option("-p", "--target-platform", dest="targetplatform",
174 help="specify special test target platform, e.g. xw_android, chrome_ubuntu"),
175 make_option("--webdriver-url", dest="wdurl", action="store",
176 help="specify special web driver listening url"),
177 make_option("--version", dest="version_info", action="store_true",
178 help="Show version information"),
179 make_option("--internal-version", dest="internal_version_info",
181 help="Show internal version information"),
182 make_option("--deviceid", dest="device_serial", action="store",
183 help="set device serial information"),
184 make_option("--testprefix", dest="test_prefix", action="store",
185 help="set prefix for test case entry"),
186 make_option("--testenvs", dest="test_env", action="store",
187 help="set environs for test case execution"),
188 make_option("--comm", dest="commodule", action="store",
189 help="set commodule by default,"
190 "set \"localhost\" for local web testing"),
191 make_option("--capability", dest="capability", action="store",
192 help="set platform for sepecfic device capability"),
193 make_option("--debug", dest="debug", action="store_true",
194 help="run in debug mode,more log information print out"),
195 make_option("--rerun", dest="rerun", action="store_true",
196 help="check if rerun test mode"),
197 make_option("--non-active", dest="non_active", action="store_true",
198 help="Disable the ability to set the result of \
199 core manual cases from the console"),
200 make_option("-d", "--debugip", dest="debugip", action="store",
201 help="specify tizen xwalk debug ip ")
206 make_option("--%s" % flt,
207 dest="w%s" % flt, action="callback", callback=varnarg,
208 help="Select the specified filter-rules : %s" % flt)
209 for flt in COMMON_FILTERS])
212 # untrusted behaviour of %%prog
213 USAGE = "%%prog [options] -f [prefix:]\"<somewhere/test.xml>\" \n\
214 forms: %%prog -f [prefix:]\"<somewhere>/test.xml\" \n\
215 %%prog -f [prefix:]\"<somewhere>/test.xml\" -D\n\
216 %%prog -f [prefix:]\"<somewhere>/test.xml\" -A\n\
217 %%prog -f [prefix:]\"<somewhere>/test.xml\" -M\n\
218 %%prog -f [prefix:]\"<somewhere>/test.xml\" --set <set_name>\n\
219 %%prog -f [prefix:]\"<somewhere>/test.xml\" --type <type_name>\n\
220 %%prog -f [prefix:]\"<somewhere>/test.xml\" --status <status_name>\n\
221 %%prog -f [prefix:]\"<somewhere>/test.xml\" --priority <priority_value>\n\
222 %%prog -f [prefix:]\"<somewhere>/test.xml\" --component <component_name>\n\
223 %%prog -f [prefix:]\"<somewhere>/test.xml\" --id <case_id>\n\
224 %%prog -f [prefix:]\"<somewhere>/test.xml\" --capability <capability_file> --comm <comm_type>\n\
225 %%prog -f [prefix:]\"<somewhere>/test.xml\" --comm <comm_type>\n\
226 %%prog -f [prefix:]\"<somewhere>/test1.xml <somewhere>/test2.xml <somewhere>/test3.xml\" \n\
227 exmaples of \"prefix\" usage: \n\
228 run a web test with a test definition (XML file) from device side: \n\
229 %%prog -f device:\"/opt/usr/media/tct/opt/tct-websocket-w3c-tests/tests.xml\" -A \n\
230 run a web test with a test definition (XML file) from localhost: \n\
231 %%prog -f \"/opt/usr/media/tct/opt/tct-websocket-w3c-tests/tests.xml\" -A \n\
232 exmaples of \"-e\" usage: \n\
233 run a web test package with TIZEN web-runtime, launcher provided in tests.xml, so \"-e\" is omitted: \n\
234 %%prog -f device:\"/opt/usr/media/tct/opt/tct-websocket-w3c-tests/tests.xml\" -A \n\
235 run a web test package with chrome browser: \n\
236 %%prog -f \"/usr/share/webapi-webkit-tests/tests.xml\" -e \
237 'google-chrome --allow-file-access-from-files --disable-web-security --start-maximized --user-data-dir=/home/test/data /home/test/webrunner/index.html' -A --comm localhost \n\
240 1) Proxy settings should be disabled when execute webapi packages\n\
241 2) TestLog is stored to %s/latest\n\
242 3) %%prog enables both auto and manual tests by default\n\
243 4) Obviously -A and -M are conflict options\n\
244 5) -e option does not support -D mode\n\
245 6) The test cases' order in the result files might be arbitrary,\
246 when running same tests.xml with same options. This is caused \
247 by python's API 'getiterator' from module 'xml.etree.ElementTree'\n\
248 7) run command 'testkit-lite', \
249 it might not be able to locate module 'testkitlite.engines.\
251 run command 'export PYTHONPATH=/usr/local/lib/python2.7/dist-packages' or \
252 run command 'export PYTHONPATH=/usr/local/lib/python2.7/site-packages' \
253 to resolve this issue" % (LOG_DIR)
258 if len(sys.argv) == 1:
259 sys.argv.append("-h")
261 PARSERS = OptionParser(option_list=OPTION_LIST, usage=USAGE)
262 (OPTIONS, ARGS) = PARSERS.parse_args()
264 # init test engine here
265 from testkitlite.util.connector import ConnectorBuilder
266 from testkitlite.util.process import get_device_lock, release_device_lock, clean_testxml
267 from testkitlite.util.session import TestSession
268 from testkitlite.util.errors import TestCaseNotFoundException, TestEngineException
271 exec_types = ["auto","manual"]
272 if OPTIONS.bautoonly and OPTIONS.bmanualonly:
273 raise ValueError("-A and -M are conflict")
274 elif OPTIONS.bautoonly:
275 exec_types.remove("manual")
276 elif OPTIONS.bmanualonly:
277 exec_types.remove("auto")
281 conn_opt['commodule'] = OPTIONS.commodule or "tizenmobile"
282 conn_opt['deviceid'] = OPTIONS.device_serial
283 CONNECTOR = ConnectorBuilder(conn_opt).get_connector()
284 if CONNECTOR == None:
287 device_id = CONNECTOR.get_device_info()['device_id']
288 os.environ['DEVICE_ID'] = device_id
289 if not OPTIONS.non_active:
290 device_locked = get_device_lock(device_id)
291 if not device_locked:
292 LOGGER.error("[ Error: Failed to get device for current session... ]\n")
295 # load profile for wedrvier
296 if OPTIONS.targetplatform:
298 exec 'from testkitlite.capability.%s import initCapability' % OPTIONS.targetplatform
299 if OPTIONS.targetplatform.upper().find('TIZEN') >= 0:
300 if not OPTIONS.debugip:
301 raise ValueError("For tizen xwalk, option --debugip is needed!")
302 webdriver_vars = initCapability('TEST_APP_ID', OPTIONS.debugip)
303 elif OPTIONS.targetplatform.upper().find('ANDROID') >= 0:
304 webdriver_vars = initCapability('TEST_PKG_NAME', 'TEST_ACTIVITY_NAME')
306 webdriver_vars = initCapability()
307 os.environ['WEBDRIVER_VARS'] = json.dumps(webdriver_vars)
309 # process test environ
311 envs = OPTIONS.test_env.replace(';',' ').split(' ')
313 env_t = env_t.strip()
316 k, v = env_t.split('=')
317 os.environ[k.strip()] = v.strip()
319 # load test defintion files
320 if "device:" in OPTIONS.testxml[0]:
321 if not CONNECTOR.is_support_remote():
322 raise ValueError("For '%s' mode, please test file without prefix 'device:' " % conn_opt['commodule'])
326 if not EXISTS(TEST_PACKAGES_DIR):
327 os.makedirs(TEST_PACKAGES_DIR)
329 LOGGER.error("[ Error: "
330 "can't create test package directory: %s, error: %s ]\n" %
331 (TEST_PACKAGES_DIR, err))
333 REMOTE_TESTLITS = OPTIONS.testxml[0]
334 REMOTE_TESTLITS = REMOTE_TESTLITS.split(':')[1]
335 TESTLISTARRARY = REMOTE_TESTLITS.split()
338 for remote_file in TESTLISTARRARY:
339 tmp_remote_file = SPLIT(remote_file)
340 tmp_remote_folder = BASENAME(tmp_remote_file[0])
341 tmp_remote_test_xml = JOIN(
342 tmp_remote_folder, tmp_remote_file[1])
343 local_test_package = JOIN(
344 TEST_PACKAGES_DIR, tmp_remote_test_xml)
345 down_status = CONNECTOR.download_file(remote_file, local_test_package)
347 LOGGER.error("Failed to get test file '%s' from device"
350 LOCALARRY.append(local_test_package)
351 OPTIONS.testxml = LOCALARRY
353 if len(OPTIONS.testxml) == 1:
354 i, start, end = 0, 0, 0
356 temp_xml = OPTIONS.testxml[0]
357 while(i < len(temp_xml)):
358 tmp = temp_xml[i:len(temp_xml)]
360 index = tmp.index(".xml") + 4 + i
363 LOCAL_TESTLISTS.append(temp_xml[start:end])
366 LOGGER.error("No xml found")
368 OPTIONS.testxml = LOCAL_TESTLISTS
372 workername = OPTIONS.worker or 'default'
374 exec "from testkitlite.engines.%s import TestWorker" % workername
375 except Exception as error:
376 raise TestEngineException(workername)
377 WORKER = TestWorker(CONNECTOR)
380 RUNNER = TestSession(CONNECTOR, WORKER)
382 RUNNER.set_global_parameters(OPTIONS)
384 if not RUNNER.get_capability(OPTIONS.capability):
388 for flt in COMMON_FILTERS:
389 if eval('OPTIONS.w%s' % flt):
390 WFILTERS[flt] = eval('OPTIONS.w%s' % flt)
391 RUNNER.add_filter_rules(**WFILTERS)
393 if not OPTIONS.testxml:
394 LOGGER.error("[ Error: not specify a test xml... ]\n")
398 if OS_VER == "Linux" or OS_VER == "Darwin":
399 SESSION = datetime.today().isoformat('-')
401 SESSION = datetime.today().strftime("%Y-%m-%d_%H_%M_%S")
402 CURRENT_LOG_DIR = JOIN(LOG_DIR, SESSION)
403 LATEST_DIR = JOIN(LOG_DIR, "latest")
405 if EXISTS(LATEST_DIR):
406 os.remove(LATEST_DIR)
407 if ISLINK(LATEST_DIR):
408 os.remove(LATEST_DIR)
409 os.makedirs(CURRENT_LOG_DIR)
410 if os.name == "posix":
411 os.symlink(CURRENT_LOG_DIR, LATEST_DIR)
413 LOGGER.error("[ Error: create session log directory: "
414 "%s failed, error: %s ]\n" % (CURRENT_LOG_DIR, err))
416 # 2) prepare run test
417 # run more than one tests.xml
418 # 1. run all auto cases from the xmls
419 # 2. run all manual cases from the xmls
420 TESTXMLS = set(OPTIONS.testxml)
424 filename = os.path.splitext(filename)[0]
425 if OS_VER == "Linux" or OS_VER == "Darwin":
426 if not filename.startswith('/'):
427 LOGGER.error("[ Error:"
428 " xml file %s should start with '/' ]" % filename)
430 file_items = filename.split('/')
432 file_items = filename.split('\\')
433 if len(file_items) < 2 or file_items[-2] == "" or file_items[-1] == "":
434 LOGGER.error("[ Error:"
435 " unable to find package name from %s ]" % t)
437 filename = file_items[-2] + '_' + file_items[-1]
438 filename = "%s.total" % BASENAME(filename)
439 resultfile = "%s.xml" % filename
440 resultfile = JOIN(CURRENT_LOG_DIR, resultfile)
443 suiteparent = ep.getroot()
444 except etree.ParseError:
445 LOGGER.error("[ Error: no case found in testxml, "
446 "pls check the test package ]\n")
448 no_test_definition = 1
449 for tf in ep.getiterator('test_definition'):
450 no_test_definition = 0
451 if no_test_definition:
452 suiteparent = etree.Element('test_definition')
453 suiteparent.tail = "\n"
454 for suite in ep.getiterator('suite'):
456 suiteparent.append(suite)
457 WFILTERS['execution_type'] = exec_types
458 RUNNER.add_filter_rules(**WFILTERS)
459 RUNNER.apply_filter(suiteparent)
460 # merge duplicated test set under suite node
462 for suite in ep.getiterator('suite'):
463 for tset in suite.getiterator('set'):
464 for testcase in tset.getiterator('testcase'):
465 tset.remove(testcase)
466 if tset.get('name') in tset_list:
469 tset_list.add(tset.get('name'))
471 with open(resultfile, 'w') as output:
472 tree = etree.ElementTree(element=suiteparent)
476 "[ Error: create filtered total result file: %s failed, "
477 "error: %s ]\n" % (resultfile, err))
479 LOGGER.error("[ test xml file '%s' not found ]" % t)
483 for e_type in exec_types:
485 WFILTERS['execution_type'] = [e_type]
486 RUNNER.add_filter_rules(**WFILTERS)
487 RUNNER.prepare_run(t, resultdir=CURRENT_LOG_DIR)
489 LOGGER.error("[ Error: prepare_run test xml: "
490 "%s from testkit-lite failed, error: %s ]\n" % (t, err))
492 START_TIME = datetime.today().strftime("%Y-%m-%d_%H_%M_%S")
494 can_merge_result = True
495 RUNNER.run_case(CURRENT_LOG_DIR)
496 except TestCaseNotFoundException, err:
497 LOGGER.info("\n[ Error: exiting testkit-lite on error: %s ]\n" % err)
499 except Exception, err:
500 clean_testxml(TESTXMLS, remote_test)
501 traceback.print_exc()
502 LOGGER.error("[ Error: run test failed, error: %s ]\n" % err)
505 RUNNER.merge_resultfile(START_TIME, CURRENT_LOG_DIR)
506 clean_testxml(TESTXMLS, remote_test)
507 LOGGER.info("[ all tasks for testkit lite are accomplished, goodbye ]")
509 except Exception, err:
510 traceback.print_exc()
511 clean_testxml(TESTXMLS,remote_test)
512 LOGGER.error("[ Error: merge result failed, error: %s ]\n" % err)
514 except (TestEngineException, KeyboardInterrupt), err:
516 LOGGER.info("\n[ exiting testkit-lite on user cancel ]\n")
518 except Exception, err:
520 LOGGER.error("\n[ Error: exiting testkit-lite due to critical error: %s ]\n" % err)
521 traceback.print_exc()