1import os 2import os.path 3import subprocess 4import sys 5import tempfile 6 7from conftest import need_to_install_package_err 8 9import pytest 10 11try: 12 import esptool # noqa: F401 13except ImportError: 14 need_to_install_package_err() 15 16IMAGES_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "images") 17 18ESP8266_BIN = "not_4_byte_aligned.bin" 19 20 21def read_image(filename): 22 with open(os.path.join(IMAGES_DIR, filename), "rb") as f: 23 return f.read() 24 25 26@pytest.mark.host_test 27class TestImageInfo: 28 def run_image_info(self, chip, file, version=None): 29 """Runs image_info on a binary file. 30 Returns the command output. 31 Filenames are relative to the 'test/images' directory. 32 """ 33 34 cmd = [ 35 sys.executable, 36 "-m", 37 "esptool", 38 "--chip", 39 chip, 40 "image_info", 41 ] 42 if version is not None: 43 cmd += ["--version", str(version)] 44 # if path was passed use the whole path 45 # if file does not exists try to use file from IMAGES_DIR directory 46 cmd += [file] if os.path.isfile(file) else ["".join([IMAGES_DIR, os.sep, file])] 47 print("\nExecuting {}".format(" ".join(cmd))) 48 49 try: 50 output = subprocess.check_output(cmd) 51 output = output.decode("utf-8") 52 print(output) # for more complete stdout logs on failure 53 assert ( 54 "warning" not in output.lower() 55 ), "image_info should not output warnings" 56 return output 57 except subprocess.CalledProcessError as e: 58 print(e.output) 59 raise 60 61 def test_v1_esp32(self): 62 out = self.run_image_info("esp32", "bootloader_esp32.bin") 63 assert "Entry point: 4009816c" in out, "Wrong entry point" 64 assert "Checksum: 83 (valid)" in out, "Invalid checksum" 65 assert "4 segments" in out, "Wrong number of segments" 66 assert ( 67 "Segment 3: len 0x01068 load 0x40078000 file_offs 0x00000b64 [CACHE_APP]" 68 in out 69 ), "Wrong segment info" 70 71 def test_v1_esp8266(self): 72 out = self.run_image_info("esp8266", ESP8266_BIN) 73 assert "Image version: 1" in out, "Wrong image version" 74 assert "Entry point: 40101844" in out, "Wrong entry point" 75 assert "Checksum: 6b (valid)" in out, "Invalid checksum" 76 assert "1 segments" in out, "Wrong number of segments" 77 assert ( 78 "Segment 1: len 0x00014 load 0x40100000 file_offs 0x00000008 [IRAM]" in out 79 ), "Wrong segment info" 80 81 def test_v2_esp32c3(self): 82 out = self.run_image_info("esp32c3", "bootloader_esp32c3.bin", "2") 83 84 # Header 85 assert "Entry point: 0x403c0000" in out, "Wrong entry point" 86 assert "Segments: 4" in out, "Wrong num of segments" 87 assert "Flash size: 2MB" in out, "Wrong flash size" 88 assert "Flash freq: 40m" in out, "Wrong flash frequency" 89 assert "Flash mode: DIO" in out, "Wrong flash mode" 90 91 # Extended header 92 assert "WP pin: 0xee (disabled)" in out, "Wrong WP pin" 93 assert "Chip ID: 5 (ESP32-C3)" in out, "Wrong chip ID" 94 assert ( 95 "clk_drv: 0x0, q_drv: 0x0, d_drv: 0x0, " 96 "cs0_drv: 0x0, hd_drv: 0x0, wp_drv: 0x0" in out 97 ), "Wrong flash pins drive settings" 98 99 assert "Minimal chip revision: v0.0" in out, "Wrong min revision" 100 assert "Maximal chip revision: v0.0" in out, "Wrong min revision" 101 102 # Segments 103 assert ( 104 "1 0x01864 0x3fcd6114 0x00000034 DRAM, BYTE_ACCESSIBLE" in out 105 ), "Wrong segment info" 106 107 # Footer 108 assert "Checksum: 0x77 (valid)" in out, "Invalid checksum" 109 assert "c0a9d6d882b65580da2e5e6347 (valid)" in out, "Invalid hash" 110 111 # Check output against individual bytes in the headers 112 hdr = read_image("bootloader_esp32c3.bin")[:8] 113 ex_hdr = read_image("bootloader_esp32c3.bin")[8:24] 114 assert f"Segments: {hdr[1]}" in out, "Wrong num of segments" 115 assert f"WP pin: {ex_hdr[0]:#02x}" in out, "Wrong WP pin" 116 assert f"Chip ID: {ex_hdr[4]}" in out, "Wrong chip ID" 117 if ex_hdr[15] == 1: # Hash appended 118 assert "Validation hash: 4faeab1bd3fd" in out, "Invalid hash" 119 120 def test_v2_esp8266(self): 121 out = self.run_image_info("esp8266", ESP8266_BIN, "2") 122 assert "Image version: 1" in out, "Wrong image version" 123 assert "Entry point: 0x40101844" in out, "Wrong entry point" 124 assert "Flash size: 512KB" in out, "Wrong flash size" 125 assert "Flash freq: 40m" in out, "Wrong flash frequency" 126 assert "Flash mode: QIO" in out, "Wrong flash mode" 127 assert "Checksum: 0x6b (valid)" in out, "Invalid checksum" 128 assert "Segments: 1" in out, "Wrong number of segments" 129 assert "0 0x00014 0x40100000 0x00000008 IRAM" in out, "Wrong segment info" 130 131 def test_image_type_detection(self): 132 # ESP8266, version 1 and 2 133 out = self.run_image_info("auto", ESP8266_BIN, "1") 134 assert "Detected image type: ESP8266" in out 135 assert "Segment 1: len 0x00014" in out 136 out = self.run_image_info("auto", ESP8266_BIN, "2") 137 assert "Detected image type: ESP8266" in out 138 assert "Flash freq: 40m" in out 139 out = self.run_image_info("auto", "esp8266_deepsleep.bin", "2") 140 assert "Detected image type: ESP8266" in out 141 142 # ESP32, with and without detection 143 out = self.run_image_info("auto", "bootloader_esp32.bin", "2") 144 assert "Detected image type: ESP32" in out 145 out = self.run_image_info( 146 "auto", "ram_helloworld/helloworld-esp32_edit.bin", "2" 147 ) 148 assert "Detected image type: ESP32" in out 149 out = self.run_image_info("esp32", "bootloader_esp32.bin", "2") 150 assert "Detected image type: ESP32" not in out 151 152 # ESP32-C3 153 out = self.run_image_info("auto", "bootloader_esp32c3.bin", "2") 154 assert "Detected image type: ESP32-C3" in out 155 156 # ESP32-S3 157 out = self.run_image_info("auto", "esp32s3_header.bin", "2") 158 assert "Detected image type: ESP32-S3" in out 159 160 def test_invalid_image_type_detection(self, capsys): 161 with pytest.raises(subprocess.CalledProcessError): 162 # Invalid image 163 self.run_image_info("auto", "one_kb.bin", "2") 164 assert ( 165 "This is not a valid image (invalid magic number: 0xed)" 166 in capsys.readouterr().out 167 ) 168 169 def test_application_info(self): 170 out = self.run_image_info("auto", "esp_idf_blink_esp32s2.bin", "2") 171 assert "Application information" in out 172 assert "Project name: blink" in out 173 assert "App version: qa-test-v5.0-20220830-4-g4532e6" in out 174 assert "Secure version: 0" in out 175 assert "Compile time: Sep 13 2022" in out 176 assert "19:46:07" in out 177 assert "3059e6b55a965865febd28fa9f6028ad5" in out 178 assert "cd0dab311febb0a3ea79eaa223ac2b0" in out 179 assert "ESP-IDF: v5.0-beta1-427-g4532e6e0b2-dirt" in out 180 # No application info in image 181 out = self.run_image_info("auto", "bootloader_esp32.bin", "2") 182 assert "Application information" not in out 183 out = self.run_image_info("auto", ESP8266_BIN, "2") 184 assert "Application information" not in out 185 186 def test_bootloader_info(self): 187 # This bootloader binary is built from "hello_world" project 188 # with default settings, IDF version is v5.2. 189 out = self.run_image_info("esp32", "bootloader_esp32_v5_2.bin", "2") 190 assert "File size: 26768 (bytes)" in out 191 assert "Bootloader information" in out 192 assert "Bootloader version: 1" in out 193 assert "ESP-IDF: v5.2-dev-254-g1950b15" in out 194 assert "Compile time: Apr 25 2023 00:13:32" in out 195 196 def test_intel_hex(self): 197 # This bootloader binary is built from "hello_world" project 198 # with default settings, IDF version is v5.2. 199 # File is converted to Intel Hex using merge_bin 200 201 def convert_bin2hex(file): 202 subprocess.check_output( 203 [ 204 sys.executable, 205 "-m", 206 "esptool", 207 "--chip", 208 "esp32", 209 "merge_bin", 210 "--format", 211 "hex", 212 "0x0", 213 "".join([IMAGES_DIR, os.sep, "bootloader_esp32_v5_2.bin"]), 214 "-o", 215 file, 216 ] 217 ) 218 219 fd, file = tempfile.mkstemp(suffix=".hex") 220 try: 221 convert_bin2hex(file) 222 out = self.run_image_info("esp32", file, "2") 223 assert "File size: 26768 (bytes)" in out 224 assert "Bootloader information" in out 225 assert "Bootloader version: 1" in out 226 assert "ESP-IDF: v5.2-dev-254-g1950b15" in out 227 assert "Compile time: Apr 25 2023 00:13:32" in out 228 finally: 229 try: 230 # make sure that file was closed before removing it 231 os.close(fd) 232 except OSError: 233 pass 234 os.unlink(file) 235