example_test.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. import http.server
  2. import multiprocessing
  3. import os
  4. import random
  5. import re
  6. import socket
  7. import ssl
  8. import struct
  9. import subprocess
  10. import ttfw_idf
  11. from RangeHTTPServer import RangeRequestHandler
  12. from tiny_test_fw import DUT, Utility
  13. server_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_cert.pem')
  14. key_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_key.pem')
  15. def get_my_ip():
  16. s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  17. s1.connect(('8.8.8.8', 80))
  18. my_ip = s1.getsockname()[0]
  19. s1.close()
  20. return my_ip
  21. def get_server_status(host_ip, port):
  22. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  23. server_status = sock.connect_ex((host_ip, port))
  24. sock.close()
  25. if server_status == 0:
  26. return True
  27. return False
  28. def https_request_handler():
  29. """
  30. Returns a request handler class that handles broken pipe exception
  31. """
  32. class RequestHandler(RangeRequestHandler):
  33. def finish(self):
  34. try:
  35. if not self.wfile.closed:
  36. self.wfile.flush()
  37. self.wfile.close()
  38. except socket.error:
  39. pass
  40. self.rfile.close()
  41. def handle(self):
  42. try:
  43. RangeRequestHandler.handle(self)
  44. except socket.error:
  45. pass
  46. return RequestHandler
  47. def start_https_server(ota_image_dir, server_ip, server_port):
  48. os.chdir(ota_image_dir)
  49. requestHandler = https_request_handler()
  50. httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
  51. httpd.socket = ssl.wrap_socket(httpd.socket,
  52. keyfile=key_file,
  53. certfile=server_file, server_side=True)
  54. httpd.serve_forever()
  55. def start_chunked_server(ota_image_dir, server_port):
  56. os.chdir(ota_image_dir)
  57. chunked_server = subprocess.Popen(['openssl', 's_server', '-WWW', '-key', key_file, '-cert', server_file, '-port', str(server_port)])
  58. return chunked_server
  59. def redirect_handler_factory(url):
  60. """
  61. Returns a request handler class that redirects to supplied `url`
  62. """
  63. class RedirectHandler(http.server.SimpleHTTPRequestHandler):
  64. def do_GET(self):
  65. print('Sending resp, URL: ' + url)
  66. self.send_response(301)
  67. self.send_header('Location', url)
  68. self.end_headers()
  69. def handle(self):
  70. try:
  71. http.server.BaseHTTPRequestHandler.handle(self)
  72. except socket.error:
  73. pass
  74. return RedirectHandler
  75. def start_redirect_server(ota_image_dir, server_ip, server_port, redirection_port):
  76. os.chdir(ota_image_dir)
  77. redirectHandler = redirect_handler_factory('https://' + server_ip + ':' + str(redirection_port) + '/advanced_https_ota.bin')
  78. httpd = http.server.HTTPServer((server_ip, server_port), redirectHandler)
  79. httpd.socket = ssl.wrap_socket(httpd.socket,
  80. keyfile=key_file,
  81. certfile=server_file, server_side=True)
  82. httpd.serve_forever()
  83. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  84. def test_examples_protocol_advanced_https_ota_example(env, extra_data):
  85. """
  86. This is a positive test case, which downloads complete binary file multiple number of times.
  87. Number of iterations can be specified in variable iterations.
  88. steps: |
  89. 1. join AP
  90. 2. Fetch OTA image over HTTPS
  91. 3. Reboot with the new OTA image
  92. """
  93. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  94. # Number of iterations to validate OTA
  95. iterations = 3
  96. server_port = 8001
  97. # File to be downloaded. This file is generated after compilation
  98. bin_name = 'advanced_https_ota.bin'
  99. # check and log bin size
  100. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  101. bin_size = os.path.getsize(binary_file)
  102. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  103. # start test
  104. host_ip = get_my_ip()
  105. if (get_server_status(host_ip, server_port) is False):
  106. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  107. thread1.daemon = True
  108. thread1.start()
  109. dut1.start_app()
  110. for i in range(iterations):
  111. dut1.expect('Loaded app from partition at offset', timeout=30)
  112. try:
  113. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  114. print('Connected to AP with IP: {}'.format(ip_address))
  115. except DUT.ExpectTimeout:
  116. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  117. thread1.terminate()
  118. dut1.expect('Starting Advanced OTA example', timeout=30)
  119. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  120. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  121. dut1.expect('Loaded app from partition at offset', timeout=60)
  122. dut1.expect('Starting Advanced OTA example', timeout=30)
  123. dut1.reset()
  124. thread1.terminate()
  125. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  126. def test_examples_protocol_advanced_https_ota_example_truncated_bin(env, extra_data):
  127. """
  128. Working of OTA if binary file is truncated is validated in this test case.
  129. Application should return with error message in this case.
  130. steps: |
  131. 1. join AP
  132. 2. Generate truncated binary file
  133. 3. Fetch OTA image over HTTPS
  134. 4. Check working of code if bin is truncated
  135. """
  136. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  137. server_port = 8001
  138. # Original binary file generated after compilation
  139. bin_name = 'advanced_https_ota.bin'
  140. # Truncated binary file to be generated from original binary file
  141. truncated_bin_name = 'truncated.bin'
  142. # Size of truncated file to be grnerated. This value can range from 288 bytes (Image header size) to size of original binary file
  143. # truncated_bin_size is set to 64000 to reduce consumed by the test case
  144. truncated_bin_size = 64000
  145. # check and log bin size
  146. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  147. f = open(binary_file, 'rb+')
  148. fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
  149. fo.write(f.read(truncated_bin_size))
  150. fo.close()
  151. f.close()
  152. binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
  153. bin_size = os.path.getsize(binary_file)
  154. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  155. # start test
  156. host_ip = get_my_ip()
  157. if (get_server_status(host_ip, server_port) is False):
  158. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  159. thread1.daemon = True
  160. thread1.start()
  161. dut1.start_app()
  162. dut1.expect('Loaded app from partition at offset', timeout=30)
  163. try:
  164. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  165. print('Connected to AP with IP: {}'.format(ip_address))
  166. except DUT.ExpectTimeout:
  167. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  168. thread1.terminate()
  169. dut1.expect('Starting Advanced OTA example', timeout=30)
  170. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
  171. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
  172. dut1.expect('Image validation failed, image is corrupted', timeout=30)
  173. os.remove(binary_file)
  174. thread1.terminate()
  175. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  176. def test_examples_protocol_advanced_https_ota_example_truncated_header(env, extra_data):
  177. """
  178. Working of OTA if headers of binary file are truncated is vaildated in this test case.
  179. Application should return with error message in this case.
  180. steps: |
  181. 1. join AP
  182. 2. Generate binary file with truncated headers
  183. 3. Fetch OTA image over HTTPS
  184. 4. Check working of code if headers are not sent completely
  185. """
  186. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  187. server_port = 8001
  188. # Original binary file generated after compilation
  189. bin_name = 'advanced_https_ota.bin'
  190. # Truncated binary file to be generated from original binary file
  191. truncated_bin_name = 'truncated_header.bin'
  192. # Size of truncated file to be grnerated. This value should be less than 288 bytes (Image header size)
  193. truncated_bin_size = 180
  194. # check and log bin size
  195. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  196. f = open(binary_file, 'rb+')
  197. fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
  198. fo.write(f.read(truncated_bin_size))
  199. fo.close()
  200. f.close()
  201. binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
  202. bin_size = os.path.getsize(binary_file)
  203. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  204. # start test
  205. host_ip = get_my_ip()
  206. if (get_server_status(host_ip, server_port) is False):
  207. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  208. thread1.daemon = True
  209. thread1.start()
  210. dut1.start_app()
  211. dut1.expect('Loaded app from partition at offset', timeout=30)
  212. try:
  213. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  214. print('Connected to AP with IP: {}'.format(ip_address))
  215. except DUT.ExpectTimeout:
  216. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  217. thread1.terminate()
  218. dut1.expect('Starting Advanced OTA example', timeout=30)
  219. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
  220. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
  221. dut1.expect('advanced_https_ota_example: esp_https_ota_read_img_desc failed', timeout=30)
  222. os.remove(binary_file)
  223. thread1.terminate()
  224. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  225. def test_examples_protocol_advanced_https_ota_example_random(env, extra_data):
  226. """
  227. Working of OTA if random data is added in binary file are validated in this test case.
  228. Magic byte verification should fail in this case.
  229. steps: |
  230. 1. join AP
  231. 2. Generate random binary image
  232. 3. Fetch OTA image over HTTPS
  233. 4. Check working of code for random binary file
  234. """
  235. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  236. server_port = 8001
  237. # Random binary file to be generated
  238. random_bin_name = 'random.bin'
  239. # Size of random binary file. 32000 is choosen, to reduce the time required to run the test-case
  240. random_bin_size = 32000
  241. # check and log bin size
  242. binary_file = os.path.join(dut1.app.binary_path, random_bin_name)
  243. fo = open(binary_file, 'wb+')
  244. # First byte of binary file is always set to zero. If first byte is generated randomly,
  245. # in some cases it may generate 0xE9 which will result in failure of testcase.
  246. fo.write(struct.pack('B', 0))
  247. for i in range(random_bin_size - 1):
  248. fo.write(struct.pack('B', random.randrange(0,255,1)))
  249. fo.close()
  250. bin_size = os.path.getsize(binary_file)
  251. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  252. # start test
  253. host_ip = get_my_ip()
  254. if (get_server_status(host_ip, server_port) is False):
  255. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  256. thread1.daemon = True
  257. thread1.start()
  258. dut1.start_app()
  259. dut1.expect('Loaded app from partition at offset', timeout=30)
  260. try:
  261. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  262. print('Connected to AP with IP: {}'.format(ip_address))
  263. except DUT.ExpectTimeout:
  264. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  265. thread1.terminate()
  266. dut1.expect('Starting Advanced OTA example', timeout=30)
  267. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name))
  268. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name)
  269. dut1.expect(re.compile(r'esp_https_ota: Mismatch chip id, expected 0, found \d'), timeout=10)
  270. os.remove(binary_file)
  271. thread1.terminate()
  272. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  273. def test_examples_protocol_advanced_https_ota_example_chunked(env, extra_data):
  274. """
  275. This is a positive test case, which downloads complete binary file multiple number of times.
  276. Number of iterations can be specified in variable iterations.
  277. steps: |
  278. 1. join AP
  279. 2. Fetch OTA image over HTTPS
  280. 3. Reboot with the new OTA image
  281. """
  282. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  283. # File to be downloaded. This file is generated after compilation
  284. bin_name = 'advanced_https_ota.bin'
  285. # check and log bin size
  286. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  287. bin_size = os.path.getsize(binary_file)
  288. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  289. # start test
  290. host_ip = get_my_ip()
  291. chunked_server = start_chunked_server(dut1.app.binary_path, 8070)
  292. dut1.start_app()
  293. dut1.expect('Loaded app from partition at offset', timeout=30)
  294. try:
  295. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  296. print('Connected to AP with IP: {}'.format(ip_address))
  297. except DUT.ExpectTimeout:
  298. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  299. dut1.expect('Starting Advanced OTA example', timeout=30)
  300. print('writing to device: {}'.format('https://' + host_ip + ':8070/' + bin_name))
  301. dut1.write('https://' + host_ip + ':8070/' + bin_name)
  302. dut1.expect('Loaded app from partition at offset', timeout=60)
  303. dut1.expect('Starting Advanced OTA example', timeout=30)
  304. chunked_server.kill()
  305. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  306. def test_examples_protocol_advanced_https_ota_example_redirect_url(env, extra_data):
  307. """
  308. This is a positive test case, which starts a server and a redirection server.
  309. Redirection server redirects http_request to different port
  310. Number of iterations can be specified in variable iterations.
  311. steps: |
  312. 1. join AP
  313. 2. Fetch OTA image over HTTPS
  314. 3. Reboot with the new OTA image
  315. """
  316. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  317. server_port = 8001
  318. # Port to which the request should be redirecetd
  319. redirection_server_port = 8081
  320. # File to be downloaded. This file is generated after compilation
  321. bin_name = 'advanced_https_ota.bin'
  322. # check and log bin size
  323. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  324. bin_size = os.path.getsize(binary_file)
  325. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  326. # start test
  327. host_ip = get_my_ip()
  328. if (get_server_status(host_ip, server_port) is False):
  329. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  330. thread1.daemon = True
  331. thread1.start()
  332. thread2 = multiprocessing.Process(target=start_redirect_server, args=(dut1.app.binary_path, host_ip, redirection_server_port, server_port))
  333. thread2.daemon = True
  334. thread2.start()
  335. dut1.start_app()
  336. dut1.expect('Loaded app from partition at offset', timeout=30)
  337. try:
  338. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  339. print('Connected to AP with IP: {}'.format(ip_address))
  340. except DUT.ExpectTimeout:
  341. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  342. thread1.terminate()
  343. thread2.terminate()
  344. dut1.expect('Starting Advanced OTA example', timeout=30)
  345. print('writing to device: {}'.format('https://' + host_ip + ':' + str(redirection_server_port) + '/' + bin_name))
  346. dut1.write('https://' + host_ip + ':' + str(redirection_server_port) + '/' + bin_name)
  347. dut1.expect('Loaded app from partition at offset', timeout=60)
  348. dut1.expect('Starting Advanced OTA example', timeout=30)
  349. dut1.reset()
  350. thread1.terminate()
  351. thread2.terminate()
  352. @ttfw_idf.idf_example_test(env_tag='Example_8Mflash_Ethernet')
  353. def test_examples_protocol_advanced_https_ota_example_anti_rollback(env, extra_data):
  354. """
  355. Working of OTA when anti_rollback is enabled and security version of new image is less than current one.
  356. Application should return with error message in this case.
  357. steps: |
  358. 1. join AP
  359. 2. Generate binary file with lower security version
  360. 3. Fetch OTA image over HTTPS
  361. 4. Check working of anti_rollback feature
  362. """
  363. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='anti_rollback')
  364. Utility.console_log('Erasing the flash on the chip')
  365. # erase the flash
  366. dut1.erase_flash()
  367. server_port = 8001
  368. # Original binary file generated after compilation
  369. bin_name = 'advanced_https_ota.bin'
  370. # Modified firmware image to lower security version in its header. This is to enable negative test case
  371. anti_rollback_bin_name = 'advanced_https_ota_lower_sec_version.bin'
  372. # check and log bin size
  373. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  374. file_size = os.path.getsize(binary_file)
  375. f = open(binary_file, 'rb+')
  376. fo = open(os.path.join(dut1.app.binary_path, anti_rollback_bin_name), 'wb+')
  377. fo.write(f.read(file_size))
  378. # Change security_version to 0 for negative test case
  379. fo.seek(36)
  380. fo.write(b'\x00')
  381. fo.close()
  382. f.close()
  383. binary_file = os.path.join(dut1.app.binary_path, anti_rollback_bin_name)
  384. bin_size = os.path.getsize(binary_file)
  385. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  386. # start test
  387. host_ip = get_my_ip()
  388. if (get_server_status(host_ip, server_port) is False):
  389. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  390. thread1.daemon = True
  391. thread1.start()
  392. dut1.start_app()
  393. # Positive Case
  394. dut1.expect('Loaded app from partition at offset', timeout=30)
  395. try:
  396. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  397. print('Connected to AP with IP: {}'.format(ip_address))
  398. except DUT.ExpectTimeout:
  399. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  400. thread1.terminate()
  401. dut1.expect('Starting Advanced OTA example', timeout=30)
  402. # Use originally generated image with secure_version=1
  403. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  404. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  405. dut1.expect('Loaded app from partition at offset', timeout=60)
  406. dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  407. dut1.expect('App is valid, rollback cancelled successfully', 30)
  408. # Negative Case
  409. dut1.expect('Starting Advanced OTA example', timeout=30)
  410. # Use modified image with secure_version=0
  411. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name))
  412. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name)
  413. dut1.expect('New firmware security version is less than eFuse programmed, 0 < 1', timeout=30)
  414. os.remove(binary_file)
  415. thread1.terminate()
  416. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  417. def test_examples_protocol_advanced_https_ota_example_partial_request(env, extra_data):
  418. """
  419. This is a positive test case, to test OTA workflow with Range HTTP header.
  420. steps: |
  421. 1. join AP
  422. 2. Fetch OTA image over HTTPS
  423. 3. Reboot with the new OTA image
  424. """
  425. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='partial_download')
  426. server_port = 8001
  427. # Size of partial HTTP request
  428. request_size = 16384
  429. # File to be downloaded. This file is generated after compilation
  430. bin_name = 'advanced_https_ota.bin'
  431. # check and log bin size
  432. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  433. bin_size = os.path.getsize(binary_file)
  434. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  435. http_requests = int((bin_size / request_size) - 1)
  436. # start test
  437. host_ip = get_my_ip()
  438. if (get_server_status(host_ip, server_port) is False):
  439. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  440. thread1.daemon = True
  441. thread1.start()
  442. dut1.start_app()
  443. dut1.expect('Loaded app from partition at offset', timeout=30)
  444. try:
  445. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  446. print('Connected to AP with IP: {}'.format(ip_address))
  447. except DUT.ExpectTimeout:
  448. Utility.console_log('ENV_TEST_FAILURE: Cannot connect to AP')
  449. raise
  450. thread1.terminate()
  451. dut1.expect('Starting Advanced OTA example', timeout=30)
  452. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  453. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  454. for _ in range(http_requests):
  455. dut1.expect('Connection closed', timeout=60)
  456. dut1.expect('Loaded app from partition at offset', timeout=60)
  457. dut1.expect('Starting Advanced OTA example', timeout=30)
  458. dut1.reset()
  459. thread1.terminate()
  460. @ttfw_idf.idf_example_test(env_tag='Example_WIFI_OTA', nightly_run=True)
  461. def test_examples_protocol_advanced_https_ota_example_nimble_gatts(env, extra_data):
  462. """
  463. Run an OTA image update while a BLE GATT Server is running in background. This GATT server will be using NimBLE Host stack.
  464. steps: |
  465. 1. join AP
  466. 2. Run BLE advertise and then GATT server.
  467. 3. Fetch OTA image over HTTPS
  468. 4. Reboot with the new OTA image
  469. """
  470. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='nimble')
  471. server_port = 8001
  472. # File to be downloaded. This file is generated after compilation
  473. bin_name = 'advanced_https_ota.bin'
  474. # check and log bin size
  475. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  476. bin_size = os.path.getsize(binary_file)
  477. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  478. # start test
  479. host_ip = get_my_ip()
  480. if (get_server_status(host_ip, server_port) is False):
  481. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  482. thread1.daemon = True
  483. thread1.start()
  484. dut1.start_app()
  485. dut1.expect('Loaded app from partition at offset', timeout=30)
  486. try:
  487. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  488. print('Connected to AP with IP: {}'.format(ip_address))
  489. except DUT.ExpectTimeout:
  490. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  491. thread1.terminate()
  492. dut1.expect('Starting Advanced OTA example', timeout=30)
  493. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  494. dut1.expect('GAP procedure initiated: advertise', timeout=30)
  495. print('Started GAP advertising.')
  496. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  497. dut1.expect('Loaded app from partition at offset', timeout=60)
  498. dut1.expect('Starting Advanced OTA example', timeout=30)
  499. dut1.reset()
  500. thread1.terminate()
  501. @ttfw_idf.idf_example_test(env_tag='Example_WIFI_OTA', nightly_run=True)
  502. def test_examples_protocol_advanced_https_ota_example_bluedroid_gatts(env, extra_data):
  503. """
  504. Run an OTA image update while a BLE GATT Server is running in background. This GATT server will be using Bluedroid Host stack.
  505. steps: |
  506. 1. join AP
  507. 2. Run BLE advertise and then GATT server.
  508. 3. Fetch OTA image over HTTPS
  509. 4. Reboot with the new OTA image
  510. """
  511. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='bluedroid')
  512. server_port = 8001
  513. # File to be downloaded. This file is generated after compilation
  514. bin_name = 'advanced_https_ota.bin'
  515. # check and log bin size
  516. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  517. bin_size = os.path.getsize(binary_file)
  518. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  519. # start test
  520. host_ip = get_my_ip()
  521. if (get_server_status(host_ip, server_port) is False):
  522. thread1 = multiprocessing.Process(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  523. thread1.daemon = True
  524. thread1.start()
  525. dut1.start_app()
  526. dut1.expect('Loaded app from partition at offset', timeout=30)
  527. try:
  528. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  529. print('Connected to AP with IP: {}'.format(ip_address))
  530. except DUT.ExpectTimeout:
  531. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  532. thread1.terminate()
  533. dut1.expect('Starting Advanced OTA example', timeout=30)
  534. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  535. dut1.expect('Started advertising.', timeout=30)
  536. print('Started GAP advertising.')
  537. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  538. dut1.expect('Loaded app from partition at offset', timeout=60)
  539. dut1.expect('Starting Advanced OTA example', timeout=30)
  540. dut1.reset()
  541. thread1.terminate()
  542. @ttfw_idf.idf_example_test(env_tag='EXAMPLE_ETH_OTA')
  543. def test_examples_protocol_advanced_https_ota_example_openssl_aligned_bin(env, extra_data):
  544. """
  545. This is a test case for esp_http_client_read with binary size multiple of 289 bytes
  546. steps: |
  547. 1. join AP
  548. 2. Fetch OTA image over HTTPS
  549. 3. Reboot with the new OTA image
  550. """
  551. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  552. # Original binary file generated after compilation
  553. bin_name = 'advanced_https_ota.bin'
  554. # Binary file aligned to DEFAULT_OTA_BUF_SIZE(289 bytes) boundary
  555. aligned_bin_name = 'aligned.bin'
  556. # check and log bin size
  557. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  558. # Original binary size
  559. bin_size = os.path.getsize(binary_file)
  560. # Dummy data required to align binary size to 289 bytes boundary
  561. dummy_data_size = 289 - (bin_size % 289)
  562. f = open(binary_file, 'rb+')
  563. fo = open(os.path.join(dut1.app.binary_path, aligned_bin_name), 'wb+')
  564. fo.write(f.read(bin_size))
  565. for _ in range(dummy_data_size):
  566. fo.write(struct.pack('B', random.randrange(0,255,1)))
  567. fo.close()
  568. f.close()
  569. # start test
  570. host_ip = get_my_ip()
  571. chunked_server = start_chunked_server(dut1.app.binary_path, 8070)
  572. dut1.start_app()
  573. dut1.expect('Loaded app from partition at offset', timeout=30)
  574. try:
  575. ip_address = dut1.expect(re.compile(r' (sta|eth) ip: ([^,]+),'), timeout=30)
  576. print('Connected to AP with IP: {}'.format(ip_address))
  577. except DUT.ExpectTimeout:
  578. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  579. dut1.expect('Starting Advanced OTA example', timeout=30)
  580. print('writing to device: {}'.format('https://' + host_ip + ':8070/' + aligned_bin_name))
  581. dut1.write('https://' + host_ip + ':8070/' + aligned_bin_name)
  582. dut1.expect('Loaded app from partition at offset', timeout=60)
  583. dut1.expect('Starting Advanced OTA example', timeout=30)
  584. chunked_server.kill()
  585. os.remove(aligned_bin_name)
  586. if __name__ == '__main__':
  587. test_examples_protocol_advanced_https_ota_example()
  588. test_examples_protocol_advanced_https_ota_example_chunked()
  589. test_examples_protocol_advanced_https_ota_example_redirect_url()
  590. test_examples_protocol_advanced_https_ota_example_truncated_bin()
  591. test_examples_protocol_advanced_https_ota_example_truncated_header()
  592. test_examples_protocol_advanced_https_ota_example_random()
  593. test_examples_protocol_advanced_https_ota_example_anti_rollback()
  594. test_examples_protocol_advanced_https_ota_example_partial_request()
  595. test_examples_protocol_advanced_https_ota_example_nimble_gatts()
  596. test_examples_protocol_advanced_https_ota_example_bluedroid_gatts()
  597. test_examples_protocol_advanced_https_ota_example_openssl_aligned_bin()