#!/usr/bin/env python from __future__ import print_function, division import unittest import struct import csv import sys import subprocess import tempfile import os import io import re try: import gen_esp32part except ImportError: sys.path.append("..") import gen_esp32part SIMPLE_CSV = """ # Name,Type,SubType,Offset,Size,Flags factory,0,2,65536,1048576, """ LONGER_BINARY_TABLE = b"" # type 0x00, subtype 0x00, # offset 64KB, size 1MB LONGER_BINARY_TABLE += b"\xAA\x50\x00\x00" + \ b"\x00\x00\x01\x00" + \ b"\x00\x00\x10\x00" + \ b"factory\0" + (b"\0" * 8) + \ b"\x00\x00\x00\x00" # type 0x01, subtype 0x20, # offset 0x110000, size 128KB LONGER_BINARY_TABLE += b"\xAA\x50\x01\x20" + \ b"\x00\x00\x11\x00" + \ b"\x00\x02\x00\x00" + \ b"data" + (b"\0" * 12) + \ b"\x00\x00\x00\x00" # type 0x10, subtype 0x00, # offset 0x150000, size 1MB LONGER_BINARY_TABLE += b"\xAA\x50\x10\x00" + \ b"\x00\x00\x15\x00" + \ b"\x00\x10\x00\x00" + \ b"second" + (b"\0" * 10) + \ b"\x00\x00\x00\x00" # MD5 checksum LONGER_BINARY_TABLE += b"\xEB\xEB" + b"\xFF" * 14 LONGER_BINARY_TABLE += b'\xf9\xbd\x06\x1b\x45\x68\x6f\x86\x57\x1a\x2c\xd5\x2a\x1d\xa6\x5b' # empty partition LONGER_BINARY_TABLE += b"\xFF" * 32 def _strip_trailing_ffs(binary_table): """ Strip all FFs down to the last 32 bytes (terminating entry) """ while binary_table.endswith(b"\xFF" * 64): binary_table = binary_table[0:len(binary_table) - 32] return binary_table class Py23TestCase(unittest.TestCase): def __init__(self, *args, **kwargs): super(Py23TestCase, self).__init__(*args, **kwargs) try: self.assertRaisesRegex except AttributeError: # assertRaisesRegexp is deprecated in Python3 but assertRaisesRegex doesn't exist in Python2 # This fix is used in order to avoid using the alias from the six library self.assertRaisesRegex = self.assertRaisesRegexp class CSVParserTests(Py23TestCase): def test_simple_partition(self): table = gen_esp32part.PartitionTable.from_csv(SIMPLE_CSV) self.assertEqual(len(table), 1) self.assertEqual(table[0].name, "factory") self.assertEqual(table[0].type, 0) self.assertEqual(table[0].subtype, 2) self.assertEqual(table[0].offset, 65536) self.assertEqual(table[0].size, 1048576) def test_require_type(self): csv = """ # Name,Type, SubType,Offset,Size ihavenotype, """ with self.assertRaisesRegex(gen_esp32part.InputError, "type"): gen_esp32part.PartitionTable.from_csv(csv) def test_type_subtype_names(self): csv_magicnumbers = """ # Name, Type, SubType, Offset, Size myapp, 0, 0,, 0x100000 myota_0, 0, 0x10,, 0x100000 myota_1, 0, 0x11,, 0x100000 myota_15, 0, 0x1f,, 0x100000 mytest, 0, 0x20,, 0x100000 myota_status, 1, 0,, 0x100000 """ csv_nomagicnumbers = """ # Name, Type, SubType, Offset, Size myapp, app, factory,, 0x100000 myota_0, app, ota_0,, 0x100000 myota_1, app, ota_1,, 0x100000 myota_15, app, ota_15,, 0x100000 mytest, app, test,, 0x100000 myota_status, data, ota,, 0x100000 """ # make two equivalent partition tables, one using # magic numbers and one using shortcuts. Ensure they match magic = gen_esp32part.PartitionTable.from_csv(csv_magicnumbers) magic.verify() nomagic = gen_esp32part.PartitionTable.from_csv(csv_nomagicnumbers) nomagic.verify() self.assertEqual(nomagic["myapp"].type, 0) self.assertEqual(nomagic["myapp"].subtype, 0) self.assertEqual(nomagic["myapp"], magic["myapp"]) self.assertEqual(nomagic["myota_0"].type, 0) self.assertEqual(nomagic["myota_0"].subtype, 0x10) self.assertEqual(nomagic["myota_0"], magic["myota_0"]) self.assertEqual(nomagic["myota_15"], magic["myota_15"]) self.assertEqual(nomagic["mytest"], magic["mytest"]) self.assertEqual(nomagic["myota_status"], magic["myota_status"]) # self.assertEqual(nomagic.to_binary(), magic.to_binary()) def test_unit_suffixes(self): csv = """ # Name, Type, Subtype, Offset, Size one_megabyte, app, factory, 64k, 1M """ t = gen_esp32part.PartitionTable.from_csv(csv) t.verify() self.assertEqual(t[0].offset, 64 * 1024) self.assertEqual(t[0].size, 1 * 1024 * 1024) def test_default_offsets(self): csv = """ # Name, Type, Subtype, Offset, Size first, app, factory,, 1M second, data, 0x15,, 1M minidata, data, 0x40,, 32K otherapp, app, factory,, 1M """ t = gen_esp32part.PartitionTable.from_csv(csv) # 'first' self.assertEqual(t[0].offset, 0x010000) # 64KB boundary as it's an app image self.assertEqual(t[0].size, 0x100000) # Size specified in CSV # 'second' self.assertEqual(t[1].offset, 0x110000) # prev offset+size self.assertEqual(t[1].size, 0x100000) # Size specified in CSV # 'minidata' self.assertEqual(t[2].offset, 0x210000) # 'otherapp' self.assertEqual(t[3].offset, 0x220000) # 64KB boundary as it's an app image def test_negative_size_to_offset(self): csv = """ # Name, Type, Subtype, Offset, Size first, app, factory, 0x10000, -2M second, data, 0x15, , 1M """ t = gen_esp32part.PartitionTable.from_csv(csv) t.verify() # 'first' self.assertEqual(t[0].offset, 0x10000) # in CSV self.assertEqual(t[0].size, 0x200000 - t[0].offset) # Up to 2M # 'second' self.assertEqual(t[1].offset, 0x200000) # prev offset+size def test_overlapping_offsets_fail(self): csv = """ first, app, factory, 0x100000, 2M second, app, ota_0, 0x200000, 1M """ with self.assertRaisesRegex(gen_esp32part.InputError, "overlap"): t = gen_esp32part.PartitionTable.from_csv(csv) t.verify() def test_unique_name_fail(self): csv = """ first, app, factory, 0x100000, 1M first, app, ota_0, 0x200000, 1M """ with self.assertRaisesRegex(gen_esp32part.InputError, "Partition names must be unique"): t = gen_esp32part.PartitionTable.from_csv(csv) t.verify() class BinaryOutputTests(Py23TestCase): def test_binary_entry(self): csv = """ first, 0x30, 0xEE, 0x100400, 0x300000 """ t = gen_esp32part.PartitionTable.from_csv(csv) tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 64 + 32) self.assertEqual(b'\xAA\x50', tb[0:2]) # magic self.assertEqual(b'\x30\xee', tb[2:4]) # type, subtype eo, es = struct.unpack("