example_test.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import http.server
  2. import os
  3. import random
  4. import re
  5. import socket
  6. import ssl
  7. import struct
  8. import subprocess
  9. from threading import Thread
  10. import ttfw_idf
  11. from RangeHTTPServer import RangeRequestHandler
  12. from tiny_test_fw import DUT, Utility
  13. server_cert = '-----BEGIN CERTIFICATE-----\n' \
  14. 'MIIDXTCCAkWgAwIBAgIJAP4LF7E72HakMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n'\
  15. 'BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n'\
  16. 'aWRnaXRzIFB0eSBMdGQwHhcNMTkwNjA3MDk1OTE2WhcNMjAwNjA2MDk1OTE2WjBF\n'\
  17. 'MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n'\
  18. 'ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n'\
  19. 'CgKCAQEAlzfCyv3mIv7TlLkObxunKfCdrJ/zgdANrsx0RBtpEPhV560hWJ0fEin0\n'\
  20. 'nIOMpJSiF9E6QsPdr6Q+eogH4XnOMU9JE+iG743N1dPfGEzJvRlyct/Ck8SswKPC\n'\
  21. '9+VXsnOdZmUw9y/xtANbURA/TspvPzz3Avv382ffffrJGh7ooOmaZSCZFlSYHLZA\n'\
  22. 'w/XlRr0sSRbLpFGY0gXjaAV8iHHiPDYLy4kZOepjV9U51xi+IGsL4w75zuMgsHyF\n'\
  23. '3nJeGYHgtGVBrkL0ZKG5udY0wcBjysjubDJC4iSlNiq2HD3fhs7j6CZddV2v845M\n'\
  24. 'lVKNxP0kO4Uj4D8r+5USWC8JKfAwxQIDAQABo1AwTjAdBgNVHQ4EFgQU6OE7ssfY\n'\
  25. 'IIPTDThiUoofUpsD5NwwHwYDVR0jBBgwFoAU6OE7ssfYIIPTDThiUoofUpsD5Nww\n'\
  26. 'DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAXIlHS/FJWfmcinUAxyBd\n'\
  27. '/xd5Lu8ykeru6oaUCci+Vk9lyoMMES7lQ+b/00d5x7AcTawkTil9EWpBTPTOTraA\n'\
  28. 'lzJMQhNKmSLk0iIoTtAJtSZgUSpIIozqK6lenxQQDsHbXKU6h+u9H6KZE8YcjsFl\n'\
  29. '6vL7sw9BVotw/VxfgjQ5OSGLgoLrdVT0z5C2qOuwOgz1c7jNiJhtMdwN+cOtnJp2\n'\
  30. 'fuBgEYyE3eeuWogvkWoDcIA8r17Ixzkpq2oJsdvZcHZPIZShPKW2SHUsl98KDemu\n'\
  31. 'y0pQyExmQUbwKE4vbFb9XuWCcL9XaOHQytyszt2DeD67AipvoBwVU7/LBOvqnsmy\n'\
  32. 'hA==\n'\
  33. '-----END CERTIFICATE-----\n'
  34. server_key = '-----BEGIN PRIVATE KEY-----\n'\
  35. 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCXN8LK/eYi/tOU\n'\
  36. 'uQ5vG6cp8J2sn/OB0A2uzHREG2kQ+FXnrSFYnR8SKfScg4yklKIX0TpCw92vpD56\n'\
  37. 'iAfhec4xT0kT6Ibvjc3V098YTMm9GXJy38KTxKzAo8L35Veyc51mZTD3L/G0A1tR\n'\
  38. 'ED9Oym8/PPcC+/fzZ999+skaHuig6ZplIJkWVJgctkDD9eVGvSxJFsukUZjSBeNo\n'\
  39. 'BXyIceI8NgvLiRk56mNX1TnXGL4gawvjDvnO4yCwfIXecl4ZgeC0ZUGuQvRkobm5\n'\
  40. '1jTBwGPKyO5sMkLiJKU2KrYcPd+GzuPoJl11Xa/zjkyVUo3E/SQ7hSPgPyv7lRJY\n'\
  41. 'Lwkp8DDFAgMBAAECggEAfBhAfQE7mUByNbxgAgI5fot9eaqR1Nf+QpJ6X2H3KPwC\n'\
  42. '02sa0HOwieFwYfj6tB1doBoNq7i89mTc+QUlIn4pHgIowHO0OGawomeKz5BEhjCZ\n'\
  43. '4XeLYGSoODary2+kNkf2xY8JTfFEcyvGBpJEwc4S2VyYgRRx+IgnumTSH+N5mIKZ\n'\
  44. 'SXWNdZIuHEmkwod+rPRXs6/r+PH0eVW6WfpINEbr4zVAGXJx2zXQwd2cuV1GTJWh\n'\
  45. 'cPVOXLu+XJ9im9B370cYN6GqUnR3fui13urYbnWnEf3syvoH/zuZkyrVChauoFf8\n'\
  46. '8EGb74/HhXK7Q2s8NRakx2c7OxQifCbcy03liUMmyQKBgQDFAob5B/66N4Q2cq/N\n'\
  47. 'MWPf98kYBYoLaeEOhEJhLQlKk0pIFCTmtpmUbpoEes2kCUbH7RwczpYko8tlKyoB\n'\
  48. '6Fn6RY4zQQ64KZJI6kQVsjkYpcP/ihnOY6rbds+3yyv+4uPX7Eh9sYZwZMggE19M\n'\
  49. 'CkFHkwAjiwqhiiSlUxe20sWmowKBgQDEfx4lxuFzA1PBPeZKGVBTxYPQf+DSLCre\n'\
  50. 'ZFg3ZmrxbCjRq1O7Lra4FXWD3dmRq7NDk79JofoW50yD8wD7I0B7opdDfXD2idO8\n'\
  51. '0dBnWUKDr2CAXyoLEINce9kJPbx4kFBQRN9PiGF7VkDQxeQ3kfS8CvcErpTKCOdy\n'\
  52. '5wOwBTwJdwKBgDiTFTeGeDv5nVoVbS67tDao7XKchJvqd9q3WGiXikeELJyuTDqE\n'\
  53. 'zW22pTwMF+m3UEAxcxVCrhMvhkUzNAkANHaOatuFHzj7lyqhO5QPbh4J3FMR0X9X\n'\
  54. 'V8VWRSg+jA/SECP9koOl6zlzd5Tee0tW1pA7QpryXscs6IEhb3ns5R2JAoGAIkzO\n'\
  55. 'RmnhEOKTzDex611f2D+yMsMfy5BKK2f4vjLymBH5TiBKDXKqEpgsW0huoi8Gq9Uu\n'\
  56. 'nvvXXAgkIyRYF36f0vUe0nkjLuYAQAWgC2pZYgNLJR13iVbol0xHJoXQUHtgiaJ8\n'\
  57. 'GLYFzjHQPqFMpSalQe3oELko39uOC1CoJCHFySECgYBeycUnRBikCO2n8DNhY4Eg\n'\
  58. '9Y3oxcssRt6ea5BZwgW2eAYi7/XqKkmxoSoOykUt3MJx9+EkkrL17bxFSpkj1tvL\n'\
  59. 'qvxn7egtsKjjgGNAxwXC4MwCvhveyUQQxtQb8AqGrGqo4jEEN0L15cnP38i2x1Uo\n'\
  60. 'muhfskWf4MABV0yTUaKcGg==\n'\
  61. '-----END PRIVATE KEY-----\n'
  62. def get_my_ip():
  63. s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  64. s1.connect(('8.8.8.8', 80))
  65. my_ip = s1.getsockname()[0]
  66. s1.close()
  67. return my_ip
  68. def get_server_status(host_ip, port):
  69. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  70. server_status = sock.connect_ex((host_ip, port))
  71. sock.close()
  72. if server_status == 0:
  73. return True
  74. return False
  75. def create_file(server_file, file_data):
  76. with open(server_file, 'w+') as file:
  77. file.write(file_data)
  78. def get_ca_cert(ota_image_dir):
  79. os.chdir(ota_image_dir)
  80. server_file = os.path.join(ota_image_dir, 'server_cert.pem')
  81. create_file(server_file, server_cert)
  82. key_file = os.path.join(ota_image_dir, 'server_key.pem')
  83. create_file(key_file, server_key)
  84. return server_file, key_file
  85. def https_request_handler():
  86. """
  87. Returns a request handler class that handles broken pipe exception
  88. """
  89. class RequestHandler(RangeRequestHandler):
  90. def finish(self):
  91. try:
  92. if not self.wfile.closed:
  93. self.wfile.flush()
  94. self.wfile.close()
  95. except socket.error:
  96. pass
  97. self.rfile.close()
  98. def handle(self):
  99. try:
  100. RangeRequestHandler.handle(self)
  101. except socket.error:
  102. pass
  103. return RequestHandler
  104. def start_https_server(ota_image_dir, server_ip, server_port):
  105. server_file, key_file = get_ca_cert(ota_image_dir)
  106. requestHandler = https_request_handler()
  107. httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
  108. httpd.socket = ssl.wrap_socket(httpd.socket,
  109. keyfile=key_file,
  110. certfile=server_file, server_side=True)
  111. httpd.serve_forever()
  112. def start_chunked_server(ota_image_dir, server_port):
  113. server_file, key_file = get_ca_cert(ota_image_dir)
  114. chunked_server = subprocess.Popen(['openssl', 's_server', '-WWW', '-key', key_file, '-cert', server_file, '-port', str(server_port)])
  115. return chunked_server
  116. def redirect_handler_factory(url):
  117. """
  118. Returns a request handler class that redirects to supplied `url`
  119. """
  120. class RedirectHandler(http.server.SimpleHTTPRequestHandler):
  121. def do_GET(self):
  122. print('Sending resp, URL: ' + url)
  123. self.send_response(301)
  124. self.send_header('Location', url)
  125. self.end_headers()
  126. def handle(self):
  127. try:
  128. http.server.BaseHTTPRequestHandler.handle(self)
  129. except socket.error:
  130. pass
  131. return RedirectHandler
  132. def start_redirect_server(ota_image_dir, server_ip, server_port, redirection_port):
  133. os.chdir(ota_image_dir)
  134. server_file, key_file = get_ca_cert(ota_image_dir)
  135. redirectHandler = redirect_handler_factory('https://' + server_ip + ':' + str(redirection_port) + '/advanced_https_ota.bin')
  136. httpd = http.server.HTTPServer((server_ip, server_port), redirectHandler)
  137. httpd.socket = ssl.wrap_socket(httpd.socket,
  138. keyfile=key_file,
  139. certfile=server_file, server_side=True)
  140. httpd.serve_forever()
  141. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  142. def test_examples_protocol_advanced_https_ota_example(env, extra_data):
  143. """
  144. This is a positive test case, which downloads complete binary file multiple number of times.
  145. Number of iterations can be specified in variable iterations.
  146. steps: |
  147. 1. join AP
  148. 2. Fetch OTA image over HTTPS
  149. 3. Reboot with the new OTA image
  150. """
  151. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  152. # Number of iterations to validate OTA
  153. iterations = 3
  154. server_port = 8001
  155. # File to be downloaded. This file is generated after compilation
  156. bin_name = 'advanced_https_ota.bin'
  157. # check and log bin size
  158. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  159. bin_size = os.path.getsize(binary_file)
  160. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  161. # start test
  162. host_ip = get_my_ip()
  163. if (get_server_status(host_ip, server_port) is False):
  164. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  165. thread1.daemon = True
  166. thread1.start()
  167. dut1.start_app()
  168. for i in range(iterations):
  169. dut1.expect('Loaded app from partition at offset', timeout=30)
  170. try:
  171. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  172. print('Connected to AP with IP: {}'.format(ip_address))
  173. except DUT.ExpectTimeout:
  174. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  175. thread1.close()
  176. dut1.expect('Starting Advanced OTA example', timeout=30)
  177. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  178. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  179. dut1.expect('Loaded app from partition at offset', timeout=60)
  180. dut1.expect('Starting Advanced OTA example', timeout=30)
  181. dut1.reset()
  182. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  183. def test_examples_protocol_advanced_https_ota_example_truncated_bin(env, extra_data):
  184. """
  185. Working of OTA if binary file is truncated is validated in this test case.
  186. Application should return with error message in this case.
  187. steps: |
  188. 1. join AP
  189. 2. Generate truncated binary file
  190. 3. Fetch OTA image over HTTPS
  191. 4. Check working of code if bin is truncated
  192. """
  193. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  194. server_port = 8001
  195. # Original binary file generated after compilation
  196. bin_name = 'advanced_https_ota.bin'
  197. # Truncated binary file to be generated from original binary file
  198. truncated_bin_name = 'truncated.bin'
  199. # Size of truncated file to be grnerated. This value can range from 288 bytes (Image header size) to size of original binary file
  200. # truncated_bin_size is set to 64000 to reduce consumed by the test case
  201. truncated_bin_size = 64000
  202. # check and log bin size
  203. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  204. f = open(binary_file, 'rb+')
  205. fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
  206. fo.write(f.read(truncated_bin_size))
  207. fo.close()
  208. f.close()
  209. binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
  210. bin_size = os.path.getsize(binary_file)
  211. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  212. # start test
  213. host_ip = get_my_ip()
  214. if (get_server_status(host_ip, server_port) is False):
  215. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  216. thread1.daemon = True
  217. thread1.start()
  218. dut1.start_app()
  219. dut1.expect('Loaded app from partition at offset', timeout=30)
  220. try:
  221. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  222. print('Connected to AP with IP: {}'.format(ip_address))
  223. except DUT.ExpectTimeout:
  224. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  225. dut1.expect('Starting Advanced OTA example', timeout=30)
  226. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
  227. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
  228. dut1.expect('Image validation failed, image is corrupted', timeout=30)
  229. os.remove(binary_file)
  230. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  231. def test_examples_protocol_advanced_https_ota_example_truncated_header(env, extra_data):
  232. """
  233. Working of OTA if headers of binary file are truncated is vaildated in this test case.
  234. Application should return with error message in this case.
  235. steps: |
  236. 1. join AP
  237. 2. Generate binary file with truncated headers
  238. 3. Fetch OTA image over HTTPS
  239. 4. Check working of code if headers are not sent completely
  240. """
  241. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  242. server_port = 8001
  243. # Original binary file generated after compilation
  244. bin_name = 'advanced_https_ota.bin'
  245. # Truncated binary file to be generated from original binary file
  246. truncated_bin_name = 'truncated_header.bin'
  247. # Size of truncated file to be grnerated. This value should be less than 288 bytes (Image header size)
  248. truncated_bin_size = 180
  249. # check and log bin size
  250. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  251. f = open(binary_file, 'rb+')
  252. fo = open(os.path.join(dut1.app.binary_path, truncated_bin_name), 'wb+')
  253. fo.write(f.read(truncated_bin_size))
  254. fo.close()
  255. f.close()
  256. binary_file = os.path.join(dut1.app.binary_path, truncated_bin_name)
  257. bin_size = os.path.getsize(binary_file)
  258. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  259. # start test
  260. host_ip = get_my_ip()
  261. if (get_server_status(host_ip, server_port) is False):
  262. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  263. thread1.daemon = True
  264. thread1.start()
  265. dut1.start_app()
  266. dut1.expect('Loaded app from partition at offset', timeout=30)
  267. try:
  268. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  269. print('Connected to AP with IP: {}'.format(ip_address))
  270. except DUT.ExpectTimeout:
  271. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  272. dut1.expect('Starting Advanced OTA example', timeout=30)
  273. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name))
  274. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + truncated_bin_name)
  275. dut1.expect('advanced_https_ota_example: esp_https_ota_read_img_desc failed', timeout=30)
  276. os.remove(binary_file)
  277. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  278. def test_examples_protocol_advanced_https_ota_example_random(env, extra_data):
  279. """
  280. Working of OTA if random data is added in binary file are validated in this test case.
  281. Magic byte verification should fail in this case.
  282. steps: |
  283. 1. join AP
  284. 2. Generate random binary image
  285. 3. Fetch OTA image over HTTPS
  286. 4. Check working of code for random binary file
  287. """
  288. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  289. server_port = 8001
  290. # Random binary file to be generated
  291. random_bin_name = 'random.bin'
  292. # Size of random binary file. 32000 is choosen, to reduce the time required to run the test-case
  293. random_bin_size = 32000
  294. # check and log bin size
  295. binary_file = os.path.join(dut1.app.binary_path, random_bin_name)
  296. fo = open(binary_file, 'wb+')
  297. # First byte of binary file is always set to zero. If first byte is generated randomly,
  298. # in some cases it may generate 0xE9 which will result in failure of testcase.
  299. fo.write(struct.pack('B', 0))
  300. for i in range(random_bin_size - 1):
  301. fo.write(struct.pack('B', random.randrange(0,255,1)))
  302. fo.close()
  303. bin_size = os.path.getsize(binary_file)
  304. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  305. # start test
  306. host_ip = get_my_ip()
  307. if (get_server_status(host_ip, server_port) is False):
  308. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  309. thread1.daemon = True
  310. thread1.start()
  311. dut1.start_app()
  312. dut1.expect('Loaded app from partition at offset', timeout=30)
  313. try:
  314. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  315. print('Connected to AP with IP: {}'.format(ip_address))
  316. except DUT.ExpectTimeout:
  317. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  318. dut1.expect('Starting Advanced OTA example', timeout=30)
  319. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name))
  320. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + random_bin_name)
  321. dut1.expect('esp_ota_ops: OTA image has invalid magic byte', timeout=10)
  322. os.remove(binary_file)
  323. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  324. def test_examples_protocol_advanced_https_ota_example_chunked(env, extra_data):
  325. """
  326. This is a positive test case, which downloads complete binary file multiple number of times.
  327. Number of iterations can be specified in variable iterations.
  328. steps: |
  329. 1. join AP
  330. 2. Fetch OTA image over HTTPS
  331. 3. Reboot with the new OTA image
  332. """
  333. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  334. # File to be downloaded. This file is generated after compilation
  335. bin_name = 'advanced_https_ota.bin'
  336. # check and log bin size
  337. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  338. bin_size = os.path.getsize(binary_file)
  339. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  340. # start test
  341. host_ip = get_my_ip()
  342. chunked_server = start_chunked_server(dut1.app.binary_path, 8070)
  343. dut1.start_app()
  344. dut1.expect('Loaded app from partition at offset', timeout=30)
  345. try:
  346. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  347. print('Connected to AP with IP: {}'.format(ip_address))
  348. except DUT.ExpectTimeout:
  349. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  350. dut1.expect('Starting Advanced OTA example', timeout=30)
  351. print('writing to device: {}'.format('https://' + host_ip + ':8070/' + bin_name))
  352. dut1.write('https://' + host_ip + ':8070/' + bin_name)
  353. dut1.expect('Loaded app from partition at offset', timeout=60)
  354. dut1.expect('Starting Advanced OTA example', timeout=30)
  355. chunked_server.kill()
  356. os.remove(os.path.join(dut1.app.binary_path, 'server_cert.pem'))
  357. os.remove(os.path.join(dut1.app.binary_path, 'server_key.pem'))
  358. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  359. def test_examples_protocol_advanced_https_ota_example_redirect_url(env, extra_data):
  360. """
  361. This is a positive test case, which starts a server and a redirection server.
  362. Redirection server redirects http_request to different port
  363. Number of iterations can be specified in variable iterations.
  364. steps: |
  365. 1. join AP
  366. 2. Fetch OTA image over HTTPS
  367. 3. Reboot with the new OTA image
  368. """
  369. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT)
  370. server_port = 8001
  371. # Port to which the request should be redirecetd
  372. redirection_server_port = 8081
  373. # File to be downloaded. This file is generated after compilation
  374. bin_name = 'advanced_https_ota.bin'
  375. # check and log bin size
  376. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  377. bin_size = os.path.getsize(binary_file)
  378. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  379. # start test
  380. host_ip = get_my_ip()
  381. if (get_server_status(host_ip, server_port) is False):
  382. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  383. thread1.daemon = True
  384. thread1.start()
  385. thread2 = Thread(target=start_redirect_server, args=(dut1.app.binary_path, host_ip, redirection_server_port, server_port))
  386. thread2.daemon = True
  387. thread2.start()
  388. dut1.start_app()
  389. dut1.expect('Loaded app from partition at offset', timeout=30)
  390. try:
  391. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  392. print('Connected to AP with IP: {}'.format(ip_address))
  393. except DUT.ExpectTimeout:
  394. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  395. thread1.close()
  396. thread2.close()
  397. dut1.expect('Starting Advanced OTA example', timeout=30)
  398. print('writing to device: {}'.format('https://' + host_ip + ':' + str(redirection_server_port) + '/' + bin_name))
  399. dut1.write('https://' + host_ip + ':' + str(redirection_server_port) + '/' + bin_name)
  400. dut1.expect('Loaded app from partition at offset', timeout=60)
  401. dut1.expect('Starting Advanced OTA example', timeout=30)
  402. dut1.reset()
  403. @ttfw_idf.idf_example_test(env_tag='Example_8Mflash_Ethernet')
  404. def test_examples_protocol_advanced_https_ota_example_anti_rollback(env, extra_data):
  405. """
  406. Working of OTA when anti_rollback is enabled and security version of new image is less than current one.
  407. Application should return with error message in this case.
  408. steps: |
  409. 1. join AP
  410. 2. Generate binary file with lower security version
  411. 3. Fetch OTA image over HTTPS
  412. 4. Check working of anti_rollback feature
  413. """
  414. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='anti_rollback')
  415. server_port = 8001
  416. # Original binary file generated after compilation
  417. bin_name = 'advanced_https_ota.bin'
  418. # Modified firmware image to lower security version in its header. This is to enable negative test case
  419. anti_rollback_bin_name = 'advanced_https_ota_lower_sec_version.bin'
  420. # check and log bin size
  421. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  422. file_size = os.path.getsize(binary_file)
  423. f = open(binary_file, 'rb+')
  424. fo = open(os.path.join(dut1.app.binary_path, anti_rollback_bin_name), 'wb+')
  425. fo.write(f.read(file_size))
  426. # Change security_version to 0 for negative test case
  427. fo.seek(36)
  428. fo.write(b'\x00')
  429. fo.close()
  430. f.close()
  431. binary_file = os.path.join(dut1.app.binary_path, anti_rollback_bin_name)
  432. bin_size = os.path.getsize(binary_file)
  433. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  434. # start test
  435. host_ip = get_my_ip()
  436. if (get_server_status(host_ip, server_port) is False):
  437. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  438. thread1.daemon = True
  439. thread1.start()
  440. dut1.start_app()
  441. # Positive Case
  442. dut1.expect('Loaded app from partition at offset', timeout=30)
  443. try:
  444. ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)
  445. print('Connected to AP with IP: {}'.format(ip_address))
  446. except DUT.ExpectTimeout:
  447. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  448. dut1.expect('Starting Advanced OTA example', timeout=30)
  449. # Use originally generated image with secure_version=1
  450. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  451. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  452. dut1.expect('Loaded app from partition at offset', timeout=60)
  453. dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30)
  454. dut1.expect('App is valid, rollback cancelled successfully', 30)
  455. # Negative Case
  456. dut1.expect('Starting Advanced OTA example', timeout=30)
  457. # Use modified image with secure_version=0
  458. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name))
  459. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + anti_rollback_bin_name)
  460. dut1.expect('New firmware security version is less than eFuse programmed, 0 < 1', timeout=30)
  461. os.remove(anti_rollback_bin_name)
  462. @ttfw_idf.idf_example_test(env_tag='Example_WIFI')
  463. def test_examples_protocol_advanced_https_ota_example_partial_request(env, extra_data):
  464. """
  465. This is a positive test case, to test OTA workflow with Range HTTP header.
  466. steps: |
  467. 1. join AP
  468. 2. Fetch OTA image over HTTPS
  469. 3. Reboot with the new OTA image
  470. """
  471. dut1 = env.get_dut('advanced_https_ota_example', 'examples/system/ota/advanced_https_ota', dut_class=ttfw_idf.ESP32DUT, app_config_name='partial_download')
  472. server_port = 8001
  473. # File to be downloaded. This file is generated after compilation
  474. bin_name = 'advanced_https_ota.bin'
  475. # check and log bin size
  476. binary_file = os.path.join(dut1.app.binary_path, bin_name)
  477. bin_size = os.path.getsize(binary_file)
  478. ttfw_idf.log_performance('advanced_https_ota_bin_size', '{}KB'.format(bin_size // 1024))
  479. http_requests = int((bin_size / 50000) + 1)
  480. # start test
  481. host_ip = get_my_ip()
  482. if (get_server_status(host_ip, server_port) is False):
  483. thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, server_port))
  484. thread1.daemon = True
  485. thread1.start()
  486. dut1.start_app()
  487. dut1.expect('Loaded app from partition at offset', timeout=30)
  488. try:
  489. ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30)
  490. print('Connected to AP with IP: {}'.format(ip_address))
  491. except DUT.ExpectTimeout:
  492. Utility.console_log('ENV_TEST_FAILURE: Cannot connect to AP')
  493. raise
  494. dut1.expect('Starting Advanced OTA example', timeout=30)
  495. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port) + '/' + bin_name))
  496. dut1.write('https://' + host_ip + ':' + str(server_port) + '/' + bin_name)
  497. for _ in range(http_requests):
  498. dut1.expect('Connection closed', timeout=60)
  499. dut1.expect('Loaded app from partition at offset', timeout=60)
  500. dut1.expect('Starting Advanced OTA example', timeout=30)
  501. dut1.reset()
  502. if __name__ == '__main__':
  503. test_examples_protocol_advanced_https_ota_example()
  504. test_examples_protocol_advanced_https_ota_example_chunked()
  505. test_examples_protocol_advanced_https_ota_example_redirect_url()
  506. test_examples_protocol_advanced_https_ota_example_truncated_bin()
  507. test_examples_protocol_advanced_https_ota_example_truncated_header()
  508. test_examples_protocol_advanced_https_ota_example_random()
  509. test_examples_protocol_advanced_https_ota_example_anti_rollback()
  510. test_examples_protocol_advanced_https_ota_example_partial_request()