Skip to content

Commit

Permalink
add tests for option generators
Browse files Browse the repository at this point in the history
  • Loading branch information
David Schneider committed May 23, 2019
1 parent 93b585d commit 866d692
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 35 deletions.
10 changes: 10 additions & 0 deletions docs/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from vbftool.vbf import Vbf
import bincopy

bin = bincopy.BinFile()
bin.add_ihex_file('test.hex')

vbf = Vbf(0x1000000, 0x3C0000, bin.as_binary())

with open('reference.vbf', 'wb') as fp:
vbf.dump(fp)
Binary file modified tests/reference.vbf
Binary file not shown.
91 changes: 91 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from io import BytesIO

import vbftool.options as opts
from vbftool import SwPartType, Network, FrameFormat


def assert_desc(desc, expected):
with BytesIO() as fp:
desc.dump(fp)
fp.seek(0)
assert fp.read() == expected


def test_description_single_line():
desc = opts.Description('description')
assert_desc(desc, b'\t// Description\r\n\tdescription = { "description" };\r\n\r\n')


def test_description_multi_line():
desc = opts.Description(['description1', 'description2'])
assert_desc(desc, b'\t// Description\r\n\tdescription = { "description1", "description2" };\r\n\r\n')


def test_sw_part_number_wers_or_eniva():
desc = opts.SwPartNumber('WERS number')
assert_desc(desc, b'\t// Software part number\r\n\tsw_part_number = "WERS number";\r\n\r\n')


def test_sw_part_number_wers_and_eniva():
desc = opts.SwPartNumber(['WERS number', "ENOVIA number"])
assert_desc(desc, b'\t// Software part number\r\n\tsw_part_number = { "WERS number", "ENOVIA number" };\r\n\r\n')


def test_sw_part_number_DID():
desc = opts.SwPartNumberDID(0xF188)
assert_desc(desc, b'\t// DID to read software part number\r\n\tsw_part_number_DID = 0xF188;\r\n\r\n')


def test_sw_part_type():
desc = opts.SwPartType(SwPartType.EXE)
assert_desc(desc, b'\t// Software part type\r\n\tsw_part_type = EXE;\r\n\r\n')


def test_data_format_identifier_none():
desc = opts.DataFormatIdentifier(0, 0)
assert_desc(desc, b'\t// Format identifier\r\n\tdata_format_identifier = 0x00;\r\n\r\n')


def test_data_format_identifier_compressed():
desc = opts.DataFormatIdentifier(1, 0)
assert_desc(desc, b'\t// Format identifier\r\n\tdata_format_identifier = 0x10;\r\n\r\n')


def test_network_single():
desc = opts.Network(Network.CAN_HS)
assert_desc(desc, b'\t// Network type or list\r\n\tnetwork = CAN_HS;\r\n\r\n')


def test_network_subnet():
desc = opts.Network([Network.CAN_HS, Network.SUB_CAN1])
assert_desc(desc, b'\t// Network type or list\r\n\tnetwork = { CAN_HS, SUB_CAN1 };\r\n\r\n')


def test_ecu_address_single():
desc = opts.EcuAddress(0x723)
assert_desc(desc, b'\t// ECU address or list\r\n\tecu_address = 0x723;\r\n\r\n')


def test_ecu_address_subnet():
desc = opts.EcuAddress([0x723, 0x740])
assert_desc(desc, b'\t// ECU address or list\r\n\tecu_address = { 0x723, 0x740 };\r\n\r\n')


def test_frame_format():
desc = opts.FrameFormat(FrameFormat.CAN_STANDARD)
assert_desc(desc, b'\t// Format frame\r\n\tframe_format = CAN_STANDARD;\r\n\r\n')


def test_erase_single_block():
desc = opts.Erase([[0x10000, 0x20000]])
assert_desc(desc, b'\t// Erase block\r\n\terase = { { 0x10000, 0x20000 } };\r\n\r\n')


def test_erase_multi_block():
desc = opts.Erase([[0x10000, 0x20000], [0x50000, 0x1000]])
assert_desc(desc, b'\t// Erase block\r\n\terase = { { 0x10000, 0x20000 }, { 0x50000, 0x1000 } };\r\n\r\n')


def test_call():
desc = opts.Call(0x10000)
assert_desc(desc, b'\t// Call address\r\n\tcall = 0x10000;\r\n\r\n')
12 changes: 7 additions & 5 deletions tests/test_vbf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import vbftool.options as opts


def test_block():
block = Vbf.create_data_block(0x1200, b'\x33\x22\x55\xAA\xBB\xCC\xDD\xEE\xFF')
assert block == b'\x00\x00\x12\x00\x00\x00\x00\x09\x33\x22\x55\xaa\xbb\xcc\xdd\xee\xff\xf5\x3f'


def test_reference():
data = array('B', range(1, 255))
start_addr = 64 * 1024 # 64 KB
Expand All @@ -16,17 +21,14 @@ def test_reference():
vbf.add_option(opts.SwPartNumber('318-08832-AB'))
vbf.add_option(opts.SwPartNumberDID(0xF188))
vbf.add_option(opts.SwPartType(SwPartType.EXE))
vbf.add_option(opts.DataFormatIdentifier(0x00))
vbf.add_option(opts.DataFormatIdentifier(0, 0))
vbf.add_option(opts.Network(Network.CAN_HS))
vbf.add_option(opts.EcuAddress(0x723))
vbf.add_option(opts.FrameFormat(FrameFormat.CAN_STANDARD))
vbf.add_option(opts.Erase([(start_addr, length)]))
vbf.add_option(opts.Call(start_addr))

ref_filename = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'reference.vbf',
)
ref_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'reference.vbf')
with open(ref_filename, 'rb') as fp:
reference = fp.read()

Expand Down
32 changes: 18 additions & 14 deletions vbftool/options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum
from vbftool.vbf import writeline, newline
from vbftool.output import writeline, newline


# Allowed identifiers:
Expand All @@ -21,37 +21,40 @@


class Option:
def __init__(self, name, description, value):
def __init__(self, name, description, value, number_format='0x%x'):
self._name = name
self._desc = description
self._value = value
self._number_format = number_format

def format_value(self, value):
def _format_value(self, value):
if isinstance(value, Enum):
value = value.value
elif isinstance(value, int):
value = '0x%x' % value
value = self._number_format % value
elif isinstance(value, str):
value = '"%s"' % value
elif isinstance(value, tuple):
x = [self.format_value(s) for s in value]
x = [self._format_value(s) for s in value]
value = '{ %s }' % ', '.join(x)
elif isinstance(value, list):
x = [self.format_value(s) for s in value]
x = [self._format_value(s) for s in value]
value = '{ %s }' % ', '.join(x)
return value

def dump(self, fp):
writeline(fp, '\t//%s' % self._desc)
writeline(fp, '\t// %s' % self._desc)

value = self.format_value(self._value)
value = self._format_value(self._value)

writeline(fp, '\t%s = %s;' % (self._name, value))
newline(fp)


class Description(Option):
def __init__(self, value):
if not isinstance(value, list):
value = [value]
super().__init__('description', 'Description', value)


Expand All @@ -63,7 +66,7 @@ def __init__(self, value):
class SwPartNumberDID(Option):
def __init__(self, value):
super().__init__('sw_part_number_DID',
'DID to read software part number', value)
'DID to read software part number', value, number_format='0x%04X')


class SwPartType(Option):
Expand All @@ -72,8 +75,9 @@ def __init__(self, value):


class DataFormatIdentifier(Option):
def __init__(self, value):
super().__init__('data_format_identifier', 'Format identifier', value)
def __init__(self, compression_method, encryption_method):
super().__init__('data_format_identifier', 'Format identifier', compression_method << 4 | encryption_method,
number_format='0x%02x')


class Network(Option):
Expand All @@ -83,7 +87,7 @@ def __init__(self, value):

class EcuAddress(Option):
def __init__(self, value):
super().__init__('ecu_address', 'ecu_address or list', value)
super().__init__('ecu_address', 'ECU address or list', value)


class FrameFormat(Option):
Expand All @@ -93,9 +97,9 @@ def __init__(self, value):

class Erase(Option):
def __init__(self, value):
super().__init__('erase', 'erase block', value)
super().__init__('erase', 'Erase block', value)


class Call(Option):
def __init__(self, value):
super().__init__('call', 'call address', value)
super().__init__('call', 'Call address', value)
6 changes: 6 additions & 0 deletions vbftool/output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def writeline(fp, s):
fp.write(b'%s\r\n' % s.encode('utf-8'))


def newline(fp):
fp.write(b'\r\n')
34 changes: 18 additions & 16 deletions vbftool/vbf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@
from enum import Enum

from vbftool.checksum import crc16, crc32


def writeline(fp, s):
fp.write(b'%s\r\n' % s.encode('utf-8'))


def newline(fp):
fp.write(b'\r\n')
from vbftool.options import Option
from vbftool.output import writeline, newline


class VbfVersion(Enum):
Expand Down Expand Up @@ -54,6 +48,11 @@ class SwPartType(Enum):
TEST = 'TEST' # Test program, (i.e. production test, diagnostics)


class _FileChecksum(Option):
def __init__(self, value):
super().__init__('file_checksum', 'file checksum', value, number_format='0x%08x')


class Vbf:
def __init__(self, version, start_addr, memory_size, data):
self.version = version
Expand All @@ -63,11 +62,8 @@ def __init__(self, version, start_addr, memory_size, data):
self._options = []

def dump(self, fp):
data_checksum = crc16(self.data)
content = struct.pack('>II', self.start_addr, len(self.data))
content += self.data
content += struct.pack('>H', data_checksum)
file_checksum = crc32(content)
content = self.create_data_block(self.start_addr, self.data)
file_checksum = _FileChecksum(crc32(content))

writeline(fp, 'vbf_version = %s;' % self.version.value)
newline(fp)
Expand All @@ -77,13 +73,19 @@ def dump(self, fp):
for option in self._options:
option.dump(fp)

writeline(fp, '\t//file checksum')
writeline(fp, '\t%s = 0x%08x;' % ('file_checksum', file_checksum))
newline(fp)
file_checksum.dump(fp)

fp.write(b'}')

fp.write(content)

@staticmethod
def create_data_block(start_addr, data):
data_checksum = crc16(data)
content = struct.pack('>II', start_addr, len(data))
content += data
content += struct.pack('>H', data_checksum)
return content

def add_option(self, option):
self._options.append(option)

0 comments on commit 866d692

Please sign in to comment.