unit_test.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2018 Espressif Systems (Shanghai) PTE LTD
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """
  17. Test script for unit test case.
  18. """
  19. import re
  20. import os
  21. import sys
  22. import time
  23. import argparse
  24. import threading
  25. # if we want to run test case outside `tiny-test-fw` folder,
  26. # we need to insert tiny-test-fw path into sys path
  27. test_fw_path = os.getenv("TEST_FW_PATH")
  28. if test_fw_path and test_fw_path not in sys.path:
  29. sys.path.insert(0, test_fw_path)
  30. import TinyFW
  31. import IDF
  32. import Utility
  33. import Env
  34. from DUT import ExpectTimeout
  35. from IDF.IDFApp import UT
  36. UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests."
  37. RESET_PATTERN = re.compile(r"(ets [\w]{3}\s+[\d]{1,2} [\d]{4} [\d]{2}:[\d]{2}:[\d]{2}[^()]*\([\w].*?\))")
  38. EXCEPTION_PATTERN = re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))")
  39. ABORT_PATTERN = re.compile(r"(abort\(\) was called at PC 0x[a-eA-E\d]{8} on core \d)")
  40. FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
  41. END_LIST_STR = r'\r?\nEnter test for running'
  42. TEST_PATTERN = re.compile(r'\((\d+)\)\s+"([^"]+)" ([^\r]+)\r?\n(' + END_LIST_STR + r')?')
  43. TEST_SUBMENU_PATTERN = re.compile(r'\s+\((\d+)\)\s+"[^"]+"\r?\n(?=(?=\()|(' + END_LIST_STR + r'))')
  44. SIMPLE_TEST_ID = 0
  45. MULTI_STAGE_ID = 1
  46. MULTI_DEVICE_ID = 2
  47. STARTUP_TIMEOUT=10
  48. DEFAULT_TIMEOUT=20
  49. def format_test_case_config(test_case_data):
  50. """
  51. convert the test case data to unified format.
  52. We need to following info to run unit test cases:
  53. 1. unit test app config
  54. 2. test case name
  55. 3. test case reset info
  56. the formatted case config is a dict, with ut app config as keys. The value is a list of test cases.
  57. Each test case is a dict with "name" and "reset" as keys. For example::
  58. case_config = {
  59. "default": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, {...}],
  60. "psram": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}],
  61. }
  62. If config is not specified for test case, then
  63. :param test_case_data: string, list, or a dictionary list
  64. :return: formatted data
  65. """
  66. case_config = dict()
  67. def parse_case(one_case_data):
  68. """ parse and format one case """
  69. def process_reset_list(reset_list):
  70. # strip space and remove white space only items
  71. _output = list()
  72. for _r in reset_list:
  73. _data = _r.strip(" ")
  74. if _data:
  75. _output.append(_data)
  76. return _output
  77. _case = dict()
  78. if isinstance(one_case_data, str):
  79. _temp = one_case_data.split(" [reset=")
  80. _case["name"] = _temp[0]
  81. try:
  82. _case["reset"] = process_reset_list(_temp[1][0:-1].split(","))
  83. except IndexError:
  84. _case["reset"] = list()
  85. elif isinstance(one_case_data, dict):
  86. _case = one_case_data.copy()
  87. assert "name" in _case
  88. if "reset" not in _case:
  89. _case["reset"] = list()
  90. else:
  91. if isinstance(_case["reset"], str):
  92. _case["reset"] = process_reset_list(_case["reset"].split(","))
  93. else:
  94. raise TypeError("Not supported type during parsing unit test case")
  95. if "config" not in _case:
  96. _case["config"] = "default"
  97. return _case
  98. if not isinstance(test_case_data, list):
  99. test_case_data = [test_case_data]
  100. for case_data in test_case_data:
  101. parsed_case = parse_case(case_data)
  102. try:
  103. case_config[parsed_case["config"]].append(parsed_case)
  104. except KeyError:
  105. case_config[parsed_case["config"]] = [parsed_case]
  106. return case_config
  107. def replace_app_bin(dut, name, new_app_bin):
  108. if new_app_bin is None:
  109. return
  110. search_pattern = '/{}.bin'.format(name)
  111. for i, config in enumerate(dut.download_config):
  112. if config.endswith(search_pattern):
  113. dut.download_config[i] = new_app_bin
  114. Utility.console_log("The replaced application binary is {}".format(new_app_bin), "O")
  115. break
  116. @IDF.idf_unit_test(env_tag="UT_T1_1")
  117. def run_unit_test_cases(env, extra_data):
  118. """
  119. extra_data can be three types of value
  120. 1. as string:
  121. 1. "case_name"
  122. 2. "case_name [reset=RESET_REASON]"
  123. 2. as dict:
  124. 1. with key like {"name": "Intr_alloc test, shared ints"}
  125. 2. with key like {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET", "config": "psram"}
  126. 3. as list of string or dict:
  127. [case1, case2, case3, {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, ...]
  128. :param extra_data: the case name or case list or case dictionary
  129. :return: None
  130. """
  131. case_config = format_test_case_config(extra_data)
  132. # we don't want stop on failed case (unless some special scenarios we can't handle)
  133. # this flag is used to log if any of the case failed during executing
  134. # Before exit test function this flag is used to log if the case fails
  135. failed_cases = []
  136. for ut_config in case_config:
  137. Utility.console_log("Running unit test for config: " + ut_config, "O")
  138. dut = env.get_dut("unit-test-app", app_path=ut_config)
  139. if len(case_config[ut_config]) > 0:
  140. replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
  141. dut.start_app()
  142. for one_case in case_config[ut_config]:
  143. dut.reset()
  144. # esptool ``run`` cmd takes quite long time.
  145. # before reset finish, serial port is closed. therefore DUT could already bootup before serial port opened.
  146. # this could cause checking bootup print failed.
  147. # now we input cmd `-`, and check either bootup print or test history,
  148. # to determine if DUT is ready to test.
  149. dut.write("-", flush=False)
  150. dut.expect_any(UT_APP_BOOT_UP_DONE,
  151. "0 Tests 0 Failures 0 Ignored", timeout=STARTUP_TIMEOUT)
  152. # run test case
  153. dut.write("\"{}\"".format(one_case["name"]))
  154. dut.expect("Running " + one_case["name"] + "...")
  155. exception_reset_list = []
  156. # we want to set this flag in callbacks (inner functions)
  157. # use list here so we can use append to set this flag
  158. test_finish = list()
  159. # expect callbacks
  160. def one_case_finish(result):
  161. """ one test finished, let expect loop break and log result """
  162. test_finish.append(True)
  163. if result:
  164. Utility.console_log("Success: " + one_case["name"], color="green")
  165. else:
  166. failed_cases.append(one_case["name"])
  167. Utility.console_log("Failed: " + one_case["name"], color="red")
  168. def handle_exception_reset(data):
  169. """
  170. just append data to exception list.
  171. exception list will be checked in ``handle_reset_finish``, once reset finished.
  172. """
  173. exception_reset_list.append(data[0])
  174. def handle_test_finish(data):
  175. """ test finished without reset """
  176. # in this scenario reset should not happen
  177. assert not exception_reset_list
  178. if int(data[1]):
  179. # case ignored
  180. Utility.console_log("Ignored: " + one_case["name"], color="orange")
  181. one_case_finish(not int(data[0]))
  182. def handle_reset_finish(data):
  183. """ reset happened and reboot finished """
  184. assert exception_reset_list # reboot but no exception/reset logged. should never happen
  185. result = False
  186. if len(one_case["reset"]) == len(exception_reset_list):
  187. for i, exception in enumerate(exception_reset_list):
  188. if one_case["reset"][i] not in exception:
  189. break
  190. else:
  191. result = True
  192. if not result:
  193. Utility.console_log("""Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}"""
  194. .format(one_case["reset"], exception_reset_list),
  195. color="orange")
  196. one_case_finish(result)
  197. while not test_finish:
  198. try:
  199. dut.expect_any((RESET_PATTERN, handle_exception_reset),
  200. (EXCEPTION_PATTERN, handle_exception_reset),
  201. (ABORT_PATTERN, handle_exception_reset),
  202. (FINISH_PATTERN, handle_test_finish),
  203. (UT_APP_BOOT_UP_DONE, handle_reset_finish),
  204. timeout=one_case["timeout"])
  205. except ExpectTimeout:
  206. Utility.console_log("Timeout in expect", color="orange")
  207. one_case_finish(False)
  208. break
  209. # raise exception if any case fails
  210. if failed_cases:
  211. Utility.console_log("Failed Cases:", color="red")
  212. for _case_name in failed_cases:
  213. Utility.console_log("\t" + _case_name, color="red")
  214. raise AssertionError("Unit Test Failed")
  215. class Handler(threading.Thread):
  216. WAIT_SIGNAL_PATTERN = re.compile(r'Waiting for signal: \[(.+)\]!')
  217. SEND_SIGNAL_PATTERN = re.compile(r'Send signal: \[(.+)\]!')
  218. FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored")
  219. def __init__(self, dut, sent_signal_list, lock, parent_case_name, child_case_index, timeout):
  220. self.dut = dut
  221. self.sent_signal_list = sent_signal_list
  222. self.lock = lock
  223. self.parent_case_name = parent_case_name
  224. self.child_case_name = ""
  225. self.child_case_index = child_case_index + 1
  226. self.finish = False
  227. self.result = False
  228. self.fail_name = None
  229. self.timeout = timeout
  230. threading.Thread.__init__(self, name="{} Handler".format(dut))
  231. def run(self):
  232. def get_child_case_name(data):
  233. self.child_case_name = data[0]
  234. time.sleep(1)
  235. self.dut.write(str(self.child_case_index))
  236. def one_device_case_finish(result):
  237. """ one test finished, let expect loop break and log result """
  238. self.finish = True
  239. self.result = result
  240. if not result:
  241. self.fail_name = self.child_case_name
  242. def device_wait_action(data):
  243. start_time = time.time()
  244. expected_signal = data[0]
  245. while not THREAD_TERMINATE_FLAG:
  246. if time.time() > start_time + self.timeout:
  247. Utility.console_log("Timeout in device for function: %s"%self.child_case_name, color="orange")
  248. break
  249. with self.lock:
  250. if expected_signal in self.sent_signal_list:
  251. self.dut.write(" ")
  252. self.sent_signal_list.remove(expected_signal)
  253. break
  254. time.sleep(0.01)
  255. def device_send_action(data):
  256. with self.lock:
  257. self.sent_signal_list.append(data[0].encode('utf-8'))
  258. def handle_device_test_finish(data):
  259. """ test finished without reset """
  260. # in this scenario reset should not happen
  261. if int(data[1]):
  262. # case ignored
  263. Utility.console_log("Ignored: " + self.child_case_name, color="orange")
  264. one_device_case_finish(not int(data[0]))
  265. self.dut.reset()
  266. self.dut.write("-", flush=False)
  267. try:
  268. self.dut.expect_any(UT_APP_BOOT_UP_DONE, "0 Tests 0 Failures 0 Ignored")
  269. time.sleep(1)
  270. self.dut.write("\"{}\"".format(self.parent_case_name))
  271. self.dut.expect("Running " + self.parent_case_name + "...")
  272. except ExpectTimeout:
  273. Utility.console_log("No case detected!", color="orange")
  274. THREAD_TERMINATE_FLAG = True
  275. while not self.finish and not THREAD_TERMINATE_FLAG:
  276. try:
  277. self.dut.expect_any((re.compile('\(' + str(self.child_case_index) + '\)\s"(\w+)"'), get_child_case_name),
  278. (self.WAIT_SIGNAL_PATTERN, device_wait_action), # wait signal pattern
  279. (self.SEND_SIGNAL_PATTERN, device_send_action), # send signal pattern
  280. (self.FINISH_PATTERN, handle_device_test_finish), # test finish pattern
  281. timeout=self.timeout)
  282. except ExpectTimeout:
  283. Utility.console_log("Timeout in expect", color="orange")
  284. one_device_case_finish(False)
  285. break
  286. def get_case_info(one_case):
  287. parent_case = one_case["name"]
  288. child_case_num = one_case["child case num"]
  289. return parent_case, child_case_num
  290. def get_dut(duts, env, name, ut_config, app_bin=None):
  291. if name in duts:
  292. dut = duts[name]
  293. else:
  294. dut = env.get_dut(name, app_path=ut_config)
  295. duts[name] = dut
  296. replace_app_bin(dut, "unit-test-app", app_bin)
  297. dut.start_app()
  298. return dut
  299. def case_run(duts, ut_config, env, one_case, failed_cases, app_bin):
  300. lock = threading.RLock()
  301. threads = []
  302. send_signal_list = []
  303. result = True
  304. parent_case, case_num = get_case_info(one_case)
  305. global THREAD_TERMINATE_FLAG
  306. THREAD_TERMINATE_FLAG = False
  307. for i in range(case_num):
  308. dut = get_dut(duts, env, "dut%d" % i, ut_config, app_bin)
  309. threads.append(Handler(dut, send_signal_list, lock,
  310. parent_case, i, one_case["timeout"]))
  311. for thread in threads:
  312. thread.setDaemon(True)
  313. thread.start()
  314. for thread in threads:
  315. thread.join()
  316. result = result and thread.result
  317. if not thread.result:
  318. THREAD_TERMINATE_FLAG = True
  319. if result:
  320. Utility.console_log("Success: " + one_case["name"], color="green")
  321. else:
  322. failed_cases.append(one_case["name"])
  323. Utility.console_log("Failed: " + one_case["name"], color="red")
  324. @IDF.idf_unit_test(env_tag="UT_T2_1")
  325. def run_multiple_devices_cases(env, extra_data):
  326. """
  327. extra_data can be two types of value
  328. 1. as dict:
  329. e.g.
  330. {"name": "gpio master/slave test example",
  331. "child case num": 2,
  332. "config": "release",
  333. "env_tag": "UT_T2_1"}
  334. 2. as list dict:
  335. e.g.
  336. [{"name": "gpio master/slave test example1",
  337. "child case num": 2,
  338. "config": "release",
  339. "env_tag": "UT_T2_1"},
  340. {"name": "gpio master/slave test example2",
  341. "child case num": 2,
  342. "config": "release",
  343. "env_tag": "UT_T2_1"}]
  344. """
  345. failed_cases = []
  346. case_config = format_test_case_config(extra_data)
  347. DUTS = {}
  348. for ut_config in case_config:
  349. Utility.console_log("Running unit test for config: " + ut_config, "O")
  350. for one_case in case_config[ut_config]:
  351. case_run(DUTS, ut_config, env, one_case, failed_cases, one_case.get('app_bin'))
  352. if failed_cases:
  353. Utility.console_log("Failed Cases:", color="red")
  354. for _case_name in failed_cases:
  355. Utility.console_log("\t" + _case_name, color="red")
  356. raise AssertionError("Unit Test Failed")
  357. @IDF.idf_unit_test(env_tag="UT_T1_1")
  358. def run_multiple_stage_cases(env, extra_data):
  359. """
  360. extra_data can be 2 types of value
  361. 1. as dict: Mandantory keys: "name" and "child case num", optional keys: "reset" and others
  362. 3. as list of string or dict:
  363. [case1, case2, case3, {"name": "restart from PRO CPU", "child case num": 2}, ...]
  364. :param extra_data: the case name or case list or case dictionary
  365. :return: None
  366. """
  367. case_config = format_test_case_config(extra_data)
  368. # we don't want stop on failed case (unless some special scenarios we can't handle)
  369. # this flag is used to log if any of the case failed during executing
  370. # Before exit test function this flag is used to log if the case fails
  371. failed_cases = []
  372. for ut_config in case_config:
  373. Utility.console_log("Running unit test for config: " + ut_config, "O")
  374. dut = env.get_dut("unit-test-app", app_path=ut_config)
  375. if len(case_config[ut_config]) > 0:
  376. replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin'))
  377. dut.start_app()
  378. for one_case in case_config[ut_config]:
  379. dut.reset()
  380. dut.write("-", flush=False)
  381. dut.expect_any(UT_APP_BOOT_UP_DONE,
  382. "0 Tests 0 Failures 0 Ignored")
  383. exception_reset_list = []
  384. for test_stage in range(one_case["child case num"]):
  385. # select multi stage test case name
  386. dut.write("\"{}\"".format(one_case["name"]))
  387. dut.expect("Running " + one_case["name"] + "...")
  388. # select test function for current stage
  389. dut.write(str(test_stage + 1))
  390. # we want to set this flag in callbacks (inner functions)
  391. # use list here so we can use append to set this flag
  392. stage_finish = list()
  393. def last_stage():
  394. return test_stage == one_case["child case num"] - 1
  395. def check_reset():
  396. if one_case["reset"]:
  397. assert exception_reset_list # reboot but no exception/reset logged. should never happen
  398. result = False
  399. if len(one_case["reset"]) == len(exception_reset_list):
  400. for i, exception in enumerate(exception_reset_list):
  401. if one_case["reset"][i] not in exception:
  402. break
  403. else:
  404. result = True
  405. if not result:
  406. Utility.console_log("""Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}"""
  407. .format(one_case["reset"], exception_reset_list),
  408. color="orange")
  409. else:
  410. # we allow omit reset in multi stage cases
  411. result = True
  412. return result
  413. # expect callbacks
  414. def one_case_finish(result):
  415. """ one test finished, let expect loop break and log result """
  416. # handle test finish
  417. result = result and check_reset()
  418. if result:
  419. Utility.console_log("Success: " + one_case["name"], color="green")
  420. else:
  421. failed_cases.append(one_case["name"])
  422. Utility.console_log("Failed: " + one_case["name"], color="red")
  423. stage_finish.append("break")
  424. def handle_exception_reset(data):
  425. """
  426. just append data to exception list.
  427. exception list will be checked in ``handle_reset_finish``, once reset finished.
  428. """
  429. exception_reset_list.append(data[0])
  430. def handle_test_finish(data):
  431. """ test finished without reset """
  432. # in this scenario reset should not happen
  433. if int(data[1]):
  434. # case ignored
  435. Utility.console_log("Ignored: " + one_case["name"], color="orange")
  436. # only passed in last stage will be regarded as real pass
  437. if last_stage():
  438. one_case_finish(not int(data[0]))
  439. else:
  440. Utility.console_log("test finished before enter last stage", color="orange")
  441. one_case_finish(False)
  442. def handle_next_stage(data):
  443. """ reboot finished. we goto next stage """
  444. if last_stage():
  445. # already last stage, should never goto next stage
  446. Utility.console_log("didn't finish at last stage", color="orange")
  447. one_case_finish(False)
  448. else:
  449. stage_finish.append("continue")
  450. while not stage_finish:
  451. try:
  452. dut.expect_any((RESET_PATTERN, handle_exception_reset),
  453. (EXCEPTION_PATTERN, handle_exception_reset),
  454. (ABORT_PATTERN, handle_exception_reset),
  455. (FINISH_PATTERN, handle_test_finish),
  456. (UT_APP_BOOT_UP_DONE, handle_next_stage),
  457. timeout=one_case["timeout"])
  458. except ExpectTimeout:
  459. Utility.console_log("Timeout in expect", color="orange")
  460. one_case_finish(False)
  461. break
  462. if stage_finish[0] == "break":
  463. # test breaks on current stage
  464. break
  465. # raise exception if any case fails
  466. if failed_cases:
  467. Utility.console_log("Failed Cases:", color="red")
  468. for _case_name in failed_cases:
  469. Utility.console_log("\t" + _case_name, color="red")
  470. raise AssertionError("Unit Test Failed")
  471. def detect_update_unit_test_info(env, extra_data, app_bin):
  472. case_config = format_test_case_config(extra_data)
  473. for ut_config in case_config:
  474. dut = env.get_dut("unit-test-app", app_path=ut_config)
  475. replace_app_bin(dut, "unit-test-app", app_bin)
  476. dut.start_app()
  477. dut.write("-", flush=False)
  478. dut.expect_any(UT_APP_BOOT_UP_DONE, "0 Tests 0 Failures 0 Ignored", timeout=STARTUP_TIMEOUT)
  479. # get the list of test cases
  480. dut.write("")
  481. dut.expect("Here's the test menu, pick your combo:", timeout=DEFAULT_TIMEOUT)
  482. def find_update_dic(name, t, timeout, child_case_num=None):
  483. for dic in extra_data:
  484. if dic['name'] == name:
  485. dic['type'] = t
  486. if 'timeout' not in dic:
  487. dic['timeout'] = timeout
  488. if child_case_num:
  489. dic['child case num'] = child_case_num
  490. try:
  491. while True:
  492. data = dut.expect(TEST_PATTERN, timeout=DEFAULT_TIMEOUT)
  493. test_case_name = data[1]
  494. m = re.search(r'\[timeout=(\d+)\]', data[2])
  495. if m:
  496. timeout = int(m.group(1))
  497. else:
  498. timeout = 30
  499. m = re.search(r'\[multi_stage\]', data[2])
  500. if m:
  501. test_case_type = MULTI_STAGE_ID
  502. else:
  503. m = re.search(r'\[multi_device\]', data[2])
  504. if m:
  505. test_case_type = MULTI_DEVICE_ID
  506. else:
  507. test_case_type = SIMPLE_TEST_ID
  508. find_update_dic(test_case_name, test_case_type, timeout)
  509. if data[3] and re.search(END_LIST_STR, data[3]):
  510. break
  511. continue
  512. # find the last submenu item
  513. data = dut.expect(TEST_SUBMENU_PATTERN, timeout=DEFAULT_TIMEOUT)
  514. find_update_dic(test_case_name, test_case_type, timeout, child_case_num=int(data[0]))
  515. if data[1] and re.search(END_LIST_STR, data[1]):
  516. break
  517. # check if the unit test case names are correct, i.e. they could be found in the device
  518. for dic in extra_data:
  519. if 'type' not in dic:
  520. raise ValueError("Unit test \"{}\" doesn't exist in the flashed device!".format(dic.get('name')))
  521. except ExpectTimeout:
  522. Utility.console_log("Timeout during getting the test list", color="red")
  523. finally:
  524. dut.close()
  525. # These options are the same for all configs, therefore there is no need to continue
  526. break
  527. if __name__ == '__main__':
  528. parser = argparse.ArgumentParser()
  529. parser.add_argument(
  530. '--repeat', '-r',
  531. help='Number of repetitions for the test(s). Default is 1.',
  532. type=int,
  533. default=1
  534. )
  535. parser.add_argument("--env_config_file", "-e",
  536. help="test env config file",
  537. default=None
  538. )
  539. parser.add_argument("--app_bin", "-b",
  540. help="application binary file for flashing the chip",
  541. default=None
  542. )
  543. parser.add_argument(
  544. 'test',
  545. help='Comma separated list of <option>:<argument> where option can be "name" (default), "child case num", \
  546. "config", "timeout".',
  547. nargs='+'
  548. )
  549. args = parser.parse_args()
  550. list_of_dicts = []
  551. for test in args.test:
  552. test_args = test.split(r',')
  553. test_dict = dict()
  554. for test_item in test_args:
  555. if len(test_item) == 0:
  556. continue
  557. pair = test_item.split(r':')
  558. if len(pair) == 1 or pair[0] is 'name':
  559. test_dict['name'] = pair[0]
  560. elif len(pair) == 2:
  561. if pair[0] == 'timeout' or pair[0] == 'child case num':
  562. test_dict[pair[0]] = int(pair[1])
  563. else:
  564. test_dict[pair[0]] = pair[1]
  565. else:
  566. raise ValueError('Error in argument item {} of {}'.format(test_item, test))
  567. test_dict['app_bin'] = args.app_bin
  568. list_of_dicts.append(test_dict)
  569. TinyFW.set_default_config(env_config_file=args.env_config_file)
  570. env_config = TinyFW.get_default_config()
  571. env_config['app'] = UT
  572. env_config['dut'] = IDF.IDFDUT
  573. env_config['test_suite_name'] = 'unit_test_parsing'
  574. env = Env.Env(**env_config)
  575. detect_update_unit_test_info(env, extra_data=list_of_dicts, app_bin=args.app_bin)
  576. for i in range(1, args.repeat+1):
  577. if args.repeat > 1:
  578. Utility.console_log("Repetition {}".format(i), color="green")
  579. for dic in list_of_dicts:
  580. t = dic.get('type', SIMPLE_TEST_ID)
  581. if t == SIMPLE_TEST_ID:
  582. run_unit_test_cases(extra_data=dic)
  583. elif t == MULTI_STAGE_ID:
  584. run_multiple_stage_cases(extra_data=dic)
  585. elif t == MULTI_DEVICE_ID:
  586. run_multiple_devices_cases(extra_data=dic)
  587. else:
  588. raise ValueError('Unknown type {} of {}'.format(t, dic.get('name')))