Files
esptool/test/test_merge_bin.py
2022-03-28 23:06:34 +02:00

201 lines
7.1 KiB
Python
Executable File

#!/usr/bin/env python
from __future__ import division, print_function
import itertools
import os
import os.path
import subprocess
import sys
import tempfile
import unittest
IMAGES_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "images")
try:
ESPTOOL_PY = os.environ["ESPTOOL_PY"]
except KeyError:
ESPTOOL_PY = os.path.join(IMAGES_DIR, "../..", "esptool/__init__.py")
# import the version of esptool we are testing with
sys.path.append(os.path.dirname(ESPTOOL_PY))
from esptool.util import byte
def read_image(filename):
with open(os.path.join(IMAGES_DIR, filename), "rb") as f:
return f.read()
class MergeBinTests(unittest.TestCase):
def run_merge_bin(self, chip, offsets_names, options=[]):
"""Run merge_bin on a list of (offset, filename) tuples
with output to a named temporary file.
Filenames are relative to the 'test/images' directory.
Returns the contents of the merged file if successful.
"""
output_file = tempfile.NamedTemporaryFile(delete=False)
try:
output_file.close()
cmd = [
sys.executable,
ESPTOOL_PY,
"--chip",
chip,
"merge_bin",
"-o",
output_file.name,
] + options
for (offset, name) in offsets_names:
cmd += [hex(offset), name]
print("Executing %s" % (" ".join(cmd)))
output = str(
subprocess.check_output(cmd, cwd=IMAGES_DIR, stderr=subprocess.STDOUT)
)
print(output)
self.assertFalse(
"warning" in output.lower(), "merge_bin should not output warnings"
)
with open(output_file.name, "rb") as f:
return f.read()
except subprocess.CalledProcessError as e:
print(e.output)
raise
finally:
os.unlink(output_file.name)
def assertAllFF(self, some_bytes):
# this may need some improving as the failed assert messages may be
# very long and/or useless!
self.assertEqual(b"\xFF" * len(some_bytes), some_bytes)
def test_simple_merge(self):
merged = self.run_merge_bin(
"esp8266",
[(0x0, "one_kb.bin"), (0x1000, "one_kb.bin"), (0x10000, "one_kb.bin")],
)
one_kb = read_image("one_kb.bin")
self.assertEqual(0x400, len(one_kb))
self.assertEqual(0x10400, len(merged))
self.assertEqual(one_kb, merged[:0x400])
self.assertEqual(one_kb, merged[0x1000:0x1400])
self.assertEqual(one_kb, merged[0x10000:])
self.assertAllFF(merged[0x400:0x1000])
self.assertAllFF(merged[0x1400:0x10000])
def test_args_out_of_order(self):
# no matter which order we supply arguments, the output should be the same
args = [(0x0, "one_kb.bin"), (0x1000, "one_kb.bin"), (0x10000, "one_kb.bin")]
merged_orders = [
self.run_merge_bin("esp8266", perm_args)
for perm_args in itertools.permutations(args)
]
for m in merged_orders:
self.assertEqual(merged_orders[0], m)
def test_error_overlap(self):
args = [(0x1000, "one_mb.bin"), (0x20000, "one_kb.bin")]
for perm_args in itertools.permutations(args):
with self.assertRaises(subprocess.CalledProcessError) as fail:
self.run_merge_bin("esp32", perm_args)
self.assertIn(b"overlap", fail.exception.output)
def test_leading_padding(self):
merged = self.run_merge_bin("esp32c3", [(0x100000, "one_mb.bin")])
self.assertAllFF(merged[:0x100000])
self.assertEqual(read_image("one_mb.bin"), merged[0x100000:])
def test_update_bootloader_params(self):
merged = self.run_merge_bin(
"esp32",
[
(0x1000, "bootloader_esp32.bin"),
(0x10000, "ram_helloworld/helloworld-esp32.bin"),
],
["--flash_size", "2MB", "--flash_mode", "dout"],
)
self.assertAllFF(merged[:0x1000])
bootloader = read_image("bootloader_esp32.bin")
helloworld = read_image("ram_helloworld/helloworld-esp32.bin")
# test the bootloader is unchanged apart from the header
# (updating the header doesn't change CRC,
# and doesn't update the SHA although it will invalidate it!)
self.assertEqual(merged[0x1010 : 0x1000 + len(bootloader)], bootloader[0x10:])
# check the individual bytes in the header are as expected
merged_hdr = merged[0x1000:0x1010]
bootloader_hdr = bootloader[:0x10]
self.assertEqual(bootloader_hdr[:2], merged_hdr[:2])
self.assertEqual(3, byte(merged_hdr, 2)) # flash mode dout
self.assertEqual(0x10, byte(merged_hdr, 3) & 0xF0) # flash size 2MB (ESP32)
self.assertEqual(
byte(bootloader_hdr, 3) & 0x0F, byte(merged_hdr, 3) & 0x0F
) # flash speed is unchanged
self.assertEqual(
bootloader_hdr[4:], merged_hdr[4:]
) # remaining field are unchanged
# check all the padding is as expected
self.assertAllFF(merged[0x1000 + len(bootloader) : 0x10000])
self.assertEqual(merged[0x10000 : 0x10000 + len(helloworld)], helloworld)
def test_target_offset(self):
merged = self.run_merge_bin(
"esp32",
[
(0x1000, "bootloader_esp32.bin"),
(0x10000, "ram_helloworld/helloworld-esp32.bin"),
],
["--target-offset", "0x1000"],
)
bootloader = read_image("bootloader_esp32.bin")
helloworld = read_image("ram_helloworld/helloworld-esp32.bin")
self.assertEqual(bootloader, merged[: len(bootloader)])
self.assertEqual(helloworld, merged[0xF000 : 0xF000 + len(helloworld)])
self.assertAllFF(merged[0x1000 + len(bootloader) : 0xF000])
def test_fill_flash_size(self):
merged = self.run_merge_bin(
"esp32c3", [(0x0, "bootloader_esp32c3.bin")], ["--fill-flash-size", "4MB"]
)
bootloader = read_image("bootloader_esp32c3.bin")
self.assertEqual(0x400000, len(merged))
self.assertEqual(bootloader, merged[: len(bootloader)])
self.assertAllFF(merged[len(bootloader) :])
def test_fill_flash_size_w_target_offset(self):
merged = self.run_merge_bin(
"esp32",
[
(0x1000, "bootloader_esp32.bin"),
(0x10000, "ram_helloworld/helloworld-esp32.bin"),
],
["--target-offset", "0x1000", "--fill-flash-size", "2MB"],
)
self.assertEqual(
0x200000 - 0x1000, len(merged)
) # full length is without target-offset arg
bootloader = read_image("bootloader_esp32.bin")
helloworld = read_image("ram_helloworld/helloworld-esp32.bin")
self.assertEqual(bootloader, merged[: len(bootloader)])
self.assertEqual(helloworld, merged[0xF000 : 0xF000 + len(helloworld)])
self.assertAllFF(merged[0xF000 + len(helloworld) :])
if __name__ == "__main__":
unittest.main(buffer=True)