<gh_stars>1-10
# -*- coding: utf-8 -*-
# (c) Copyright 2019 Sensirion AG, Switzerland

from __future__ import absolute_import, division, print_function
from sensirion_shdlc_driver.firmware_image import ShdlcFirmwareImage
from sensirion_shdlc_driver.types import FirmwareVersion
from sensirion_shdlc_driver.errors import ShdlcFirmwareImageSignatureError
from intelhex import IntelHex
import os
import pytest


TESTS_ROOT = os.path.dirname(os.path.realpath(__file__))
DATA_DIR = os.path.join(TESTS_ROOT, 'data')

EKS2 = {
    'HEXFILE': os.path.join(DATA_DIR, 'Eks2_combined_V5.2.hex'),
    'PRODUCTTYPE': 0x00060000,
    'BL_ADDR': 0x08000000,
    'BL_MAJOR': 0,
    'BL_MINOR': 4,
    'APP_ADDR': 0x08004000,
    'APP_MAJOR': 5,
    'APP_MINOR': 2,
    'APP_SIZE': 75096,
    'APP_CHECKSUM': 0x88,
}

STM32G0 = {
    'HEXFILE': os.path.join(DATA_DIR, 'Stm32g0Firmware.hex'),
    'PRODUCTTYPE': 0x00140000,
    'BL_ADDR': 0x08000000,
    'BL_MAJOR': 1,
    'BL_MINOR': 0,
    'APP_ADDR': 0x08001000,
    'APP_MAJOR': 0,
    'APP_MINOR': 1,
    'APP_SIZE': 1600,
    'APP_CHECKSUM': 0x3A,
    'BL_VERSION_OFFSET': 0x200,
    'SIGNATURE': b'\x4b\x4f\x47\x4a\xa4\x74\xf4\xb4',
}


@pytest.fixture(params=[EKS2, STM32G0])
def fw(request):
    config = request.param
    image = ShdlcFirmwareImage(
        hexfile=config.get('HEXFILE'),
        bl_start_addr=config.get('BL_ADDR'),
        app_start_addr=config.get('APP_ADDR'),
        signature=config.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
        bl_version_offset=config.get('BL_VERSION_OFFSET', 0x1004))
    config['IMAGE'] = image
    yield config


def test_load_by_file_path():
    """
    Test if the image can be loaded by passing a filepath as string.
    """
    image = ShdlcFirmwareImage(
        EKS2.get('HEXFILE'), EKS2.get('BL_ADDR'), EKS2.get('APP_ADDR'))
    assert image.size > 0


def test_load_by_file_object():
    """
    Test if the image can be loaded by passing a file-like object.
    """
    with open(EKS2.get('HEXFILE'), mode='r') as f:
        image = ShdlcFirmwareImage(f, EKS2.get('BL_ADDR'),
                                   EKS2.get('APP_ADDR'))
        assert image.size > 0


@pytest.mark.parametrize("config", [EKS2, STM32G0])
def test_load_by_file_object_with_all_parameters(config):
    """
    Test if the image can be loaded by passing a file-like object.
    """
    with open(config.get('HEXFILE'), mode='r') as f:
        image = ShdlcFirmwareImage(
            hexfile=f, bl_start_addr=config.get('BL_ADDR'),
            app_start_addr=config.get('APP_ADDR'),
            signature=config.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=config.get('BL_VERSION_OFFSET', 0x1004))
        assert image.size > 0


def test_invalid_signature_raises_exception(fw):
    """
    Test if a hexfile with invalid signature raises the proper exception. The
    wrong signature is simulated by passing a wrong application start address.
    """
    with pytest.raises(ShdlcFirmwareImageSignatureError):
        ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR') + 1,
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))


def test_property_product_type(fw):
    """
    Test if the value and type of the "product_type" property is correct.
    """
    assert type(fw.get('IMAGE').product_type) is int
    assert fw.get('IMAGE').product_type == fw.get('PRODUCTTYPE')


def test_property_bootloader_version(fw):
    """
    Test if the value and type of the "bootloader_version" property is correct.
    """
    assert type(fw.get('IMAGE').bootloader_version) is FirmwareVersion
    assert fw.get('IMAGE').bootloader_version.major == fw.get('BL_MAJOR')
    assert fw.get('IMAGE').bootloader_version.minor == fw.get('BL_MINOR')
    assert fw.get('IMAGE').bootloader_version.debug is False


def test_property_application_version(fw):
    """
    Test if the value and type of the "application_version" property is
    correct.
    """
    assert type(fw.get('IMAGE').application_version) is FirmwareVersion
    assert fw.get('IMAGE').application_version.major == fw.get('APP_MAJOR')
    assert fw.get('IMAGE').application_version.minor == fw.get('APP_MINOR')
    assert fw.get('IMAGE').application_version.debug is False


def test_property_checksum(fw):
    """
    Test if the value and type of the "checksum" property is correct.
    """
    assert type(fw.get('IMAGE').checksum) is int
    assert fw.get('IMAGE').checksum == fw.get('APP_CHECKSUM')


def test_property_size(fw):
    """
    Test if the value and type of the "size" property is correct.
    """
    assert type(fw.get('IMAGE').size) is int
    assert fw.get('IMAGE').size == fw.get('APP_SIZE')


def test_property_available_bytes(fw):
    """
    Test if the value and type of the "available_bytes" property is correct.
    """
    assert type(fw.get('IMAGE').available_bytes) is int
    assert fw.get('IMAGE').available_bytes == fw.get('IMAGE').size


def test_read_all(fw):
    """
    Test if the return value and type of the "read()" method is correct.
    """
    data = fw.get('IMAGE').read()
    assert type(data) is bytes
    assert len(data) == fw.get('IMAGE').size


def test_read_blocks(fw):
    """
    Test reading data in multiple blocks.
    """
    image = ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR'),
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))
    data_read_all = image.read()

    image = ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR'),
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))
    data_read_blocks = bytes()
    available_bytes = image.available_bytes
    while image.available_bytes > 0:
        block = image.read(42)
        data_read_blocks += block
        available_bytes -= len(block)
        assert type(block) is bytes
        assert 0 <= len(block) <= 42
        assert image.available_bytes == available_bytes

    assert data_read_blocks == data_read_all


def test_read_skips_bootloader_and_signature(fw):
    """
    Test if the "read()" method does not return the bootloader content and the
    signature. This is important to keep the bootloader working after a failed
    firmware update, so it's not possible to break devices.
    """
    hexfile = IntelHex(fw.get('HEXFILE'))
    data = fw.get('IMAGE').read()
    assert len(data) == hexfile.maxaddr() - fw.get('APP_ADDR') - \
        len(fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B')) + 1

# -*- coding: utf-8 -*-
# (c) Copyright 2019 Sensirion AG, Switzerland

from __future__ import absolute_import, division, print_function
from sensirion_shdlc_driver.firmware_image import ShdlcFirmwareImage
from sensirion_shdlc_driver.types import FirmwareVersion
from sensirion_shdlc_driver.errors import ShdlcFirmwareImageSignatureError
from intelhex import IntelHex
import os
import pytest


TESTS_ROOT = os.path.dirname(os.path.realpath(__file__))
DATA_DIR = os.path.join(TESTS_ROOT, 'data')

EKS2 = {
    'HEXFILE': os.path.join(DATA_DIR, 'Eks2_combined_V5.2.hex'),
    'PRODUCTTYPE': 0x00060000,
    'BL_ADDR': 0x08000000,
    'BL_MAJOR': 0,
    'BL_MINOR': 4,
    'APP_ADDR': 0x08004000,
    'APP_MAJOR': 5,
    'APP_MINOR': 2,
    'APP_SIZE': 75096,
    'APP_CHECKSUM': 0x88,
}

STM32G0 = {
    'HEXFILE': os.path.join(DATA_DIR, 'Stm32g0Firmware.hex'),
    'PRODUCTTYPE': 0x00140000,
    'BL_ADDR': 0x08000000,
    'BL_MAJOR': 1,
    'BL_MINOR': 0,
    'APP_ADDR': 0x08001000,
    'APP_MAJOR': 0,
    'APP_MINOR': 1,
    'APP_SIZE': 1600,
    'APP_CHECKSUM': 0x3A,
    'BL_VERSION_OFFSET': 0x200,
    'SIGNATURE': b'\x4b\x4f\x47\x4a\xa4\x74\xf4\xb4',
}


@pytest.fixture(params=[EKS2, STM32G0])
def fw(request):
    config = request.param
    image = ShdlcFirmwareImage(
        hexfile=config.get('HEXFILE'),
        bl_start_addr=config.get('BL_ADDR'),
        app_start_addr=config.get('APP_ADDR'),
        signature=config.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
        bl_version_offset=config.get('BL_VERSION_OFFSET', 0x1004))
    config['IMAGE'] = image
    yield config


def test_load_by_file_path():
    """
    Test if the image can be loaded by passing a filepath as string.
    """
    image = ShdlcFirmwareImage(
        EKS2.get('HEXFILE'), EKS2.get('BL_ADDR'), EKS2.get('APP_ADDR'))
    assert image.size > 0


def test_load_by_file_object():
    """
    Test if the image can be loaded by passing a file-like object.
    """
    with open(EKS2.get('HEXFILE'), mode='r') as f:
        image = ShdlcFirmwareImage(f, EKS2.get('BL_ADDR'),
                                   EKS2.get('APP_ADDR'))
        assert image.size > 0


@pytest.mark.parametrize("config", [EKS2, STM32G0])
def test_load_by_file_object_with_all_parameters(config):
    """
    Test if the image can be loaded by passing a file-like object.
    """
    with open(config.get('HEXFILE'), mode='r') as f:
        image = ShdlcFirmwareImage(
            hexfile=f, bl_start_addr=config.get('BL_ADDR'),
            app_start_addr=config.get('APP_ADDR'),
            signature=config.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=config.get('BL_VERSION_OFFSET', 0x1004))
        assert image.size > 0


def test_invalid_signature_raises_exception(fw):
    """
    Test if a hexfile with invalid signature raises the proper exception. The
    wrong signature is simulated by passing a wrong application start address.
    """
    with pytest.raises(ShdlcFirmwareImageSignatureError):
        ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR') + 1,
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))


def test_property_product_type(fw):
    """
    Test if the value and type of the "product_type" property is correct.
    """
    assert type(fw.get('IMAGE').product_type) is int
    assert fw.get('IMAGE').product_type == fw.get('PRODUCTTYPE')


def test_property_bootloader_version(fw):
    """
    Test if the value and type of the "bootloader_version" property is correct.
    """
    assert type(fw.get('IMAGE').bootloader_version) is FirmwareVersion
    assert fw.get('IMAGE').bootloader_version.major == fw.get('BL_MAJOR')
    assert fw.get('IMAGE').bootloader_version.minor == fw.get('BL_MINOR')
    assert fw.get('IMAGE').bootloader_version.debug is False


def test_property_application_version(fw):
    """
    Test if the value and type of the "application_version" property is
    correct.
    """
    assert type(fw.get('IMAGE').application_version) is FirmwareVersion
    assert fw.get('IMAGE').application_version.major == fw.get('APP_MAJOR')
    assert fw.get('IMAGE').application_version.minor == fw.get('APP_MINOR')
    assert fw.get('IMAGE').application_version.debug is False


def test_property_checksum(fw):
    """
    Test if the value and type of the "checksum" property is correct.
    """
    assert type(fw.get('IMAGE').checksum) is int
    assert fw.get('IMAGE').checksum == fw.get('APP_CHECKSUM')


def test_property_size(fw):
    """
    Test if the value and type of the "size" property is correct.
    """
    assert type(fw.get('IMAGE').size) is int
    assert fw.get('IMAGE').size == fw.get('APP_SIZE')


def test_property_available_bytes(fw):
    """
    Test if the value and type of the "available_bytes" property is correct.
    """
    assert type(fw.get('IMAGE').available_bytes) is int
    assert fw.get('IMAGE').available_bytes == fw.get('IMAGE').size


def test_read_all(fw):
    """
    Test if the return value and type of the "read()" method is correct.
    """
    data = fw.get('IMAGE').read()
    assert type(data) is bytes
    assert len(data) == fw.get('IMAGE').size


def test_read_blocks(fw):
    """
    Test reading data in multiple blocks.
    """
    image = ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR'),
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))
    data_read_all = image.read()

    image = ShdlcFirmwareImage(
            hexfile=fw.get('HEXFILE'),
            bl_start_addr=fw.get('BL_ADDR'),
            app_start_addr=fw.get('APP_ADDR'),
            signature=fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B'),
            bl_version_offset=fw.get('BL_VERSION_OFFSET', 0x1004))
    data_read_blocks = bytes()
    available_bytes = image.available_bytes
    while image.available_bytes > 0:
        block = image.read(42)
        data_read_blocks += block
        available_bytes -= len(block)
        assert type(block) is bytes
        assert 0 <= len(block) <= 42
        assert image.available_bytes == available_bytes

    assert data_read_blocks == data_read_all


def test_read_skips_bootloader_and_signature(fw):
    """
    Test if the "read()" method does not return the bootloader content and the
    signature. This is important to keep the bootloader working after a failed
    firmware update, so it's not possible to break devices.
    """
    hexfile = IntelHex(fw.get('HEXFILE'))
    data = fw.get('IMAGE').read()
    assert len(data) == hexfile.maxaddr() - fw.get('APP_ADDR') - \
        len(fw.get('SIGNATURE', b'\x4A\x47\x4F\x4B')) + 1
# -*- coding: utf-8 -*- # (c) Copyright 2019 Sensirion AG, Switzerland Test if the image can be loaded by passing a filepath as string. Test if the image can be loaded by passing a file-like object. Test if the image can be loaded by passing a file-like object. Test if a hexfile with invalid signature raises the proper exception. The
     wrong signature is simulated by passing a wrong application start address. Test if the value and type of the "product_type" property is correct. Test if the value and type of the "bootloader_version" property is correct. Test if the value and type of the "application_version" property is
     correct. Test if the value and type of the "checksum" property is correct. Test if the value and type of the "size" property is correct. Test if the value and type of the "available_bytes" property is correct. Test if the return value and type of the "read()" method is correct. Test reading data in multiple blocks. Test if the "read()" method does not return the bootloader content and the
     signature. This is important to keep the bootloader working after a failed
     firmware update, so it's not possible to break devices.