# Copyright (c) 2019 Thomas Kupper
#
# SPDX-License-Identifier: Apache-2.0

import argparse
import os
import platform
from unittest.mock import patch, call

import pytest

from runners.stm32flash import Stm32flashBinaryRunner
from conftest import RC_KERNEL_BIN

TEST_CMD = 'stm32flash'

TEST_DEVICE = '/dev/ttyUSB0'
if platform.system() == 'Darwin':
    TEST_DEVICE = '/dev/tty.SLAB_USBtoUART'
TEST_BAUD = '115200'
TEST_FORCE_BINARY = False
TEST_ADDR = '0x08000000'
TEST_BIN_SIZE = '4095'
TEST_EXEC_ADDR = '0'
TEST_SERIAL_MODE = '8e1'
TEST_RESET = False
TEST_VERIFY = False

# Expected subprocesses to be run for each action. Using the
# runner_config fixture (and always specifying all necessary
# parameters) means we don't get 100% coverage, but it's a
# starting out point.
EXPECTED_COMMANDS = {
    'info':
    ([TEST_CMD,
      '-b', TEST_BAUD,
      '-m', TEST_SERIAL_MODE,
      TEST_DEVICE],),
    'erase':
    ([TEST_CMD,
      '-b', TEST_BAUD,
      '-m', TEST_SERIAL_MODE,
      '-S', TEST_ADDR + ":" + str((int(TEST_BIN_SIZE)  >> 12) + 1 << 12),
      '-o', TEST_DEVICE],),
    'start':
    ([TEST_CMD,
      '-b', TEST_BAUD,
      '-m', TEST_SERIAL_MODE,
      '-g', TEST_EXEC_ADDR, TEST_DEVICE],),

    'write':
    ([TEST_CMD,
      '-b', TEST_BAUD,
      '-m', TEST_SERIAL_MODE,
      '-S', TEST_ADDR + ":" + TEST_BIN_SIZE,
      '-w', RC_KERNEL_BIN,
      TEST_DEVICE],),
}

def require_patch(program):
    assert program == TEST_CMD

def os_path_getsize_patch(filename):
    if filename == RC_KERNEL_BIN:
        return TEST_BIN_SIZE
    return os.path.isfile(filename)

os_path_isfile = os.path.isfile

def os_path_isfile_patch(filename):
    if filename == RC_KERNEL_BIN:
        return True
    return os_path_isfile(filename)

@pytest.mark.parametrize('action', EXPECTED_COMMANDS)
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_stm32flash_init(cc, req, action, runner_config):
    '''Test actions using a runner created by constructor.'''
    test_exec_addr = TEST_EXEC_ADDR
    if action == 'write':
        test_exec_addr = None

    runner = Stm32flashBinaryRunner(runner_config, device=TEST_DEVICE,
                 action=action, baud=TEST_BAUD, force_binary=TEST_FORCE_BINARY,
                 start_addr=TEST_ADDR, exec_addr=test_exec_addr,
                 serial_mode=TEST_SERIAL_MODE, reset=TEST_RESET, verify=TEST_VERIFY)

    with patch('os.path.getsize', side_effect=os_path_getsize_patch):
        with patch('os.path.isfile', side_effect=os_path_isfile_patch):
            runner.run('flash')
    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS[action]]

@pytest.mark.parametrize('action', EXPECTED_COMMANDS)
@patch('runners.core.ZephyrBinaryRunner.require', side_effect=require_patch)
@patch('runners.core.ZephyrBinaryRunner.check_call')
def test_stm32flash_create(cc, req, action, runner_config):
    '''Test actions using a runner created from action line parameters.'''
    if action == 'start':
        args = ['--action', action, '--baud-rate', TEST_BAUD, '--start-addr', TEST_ADDR,
                '--execution-addr', TEST_EXEC_ADDR]
    else:
        args = ['--action', action, '--baud-rate', TEST_BAUD, '--start-addr', TEST_ADDR]

    parser = argparse.ArgumentParser(allow_abbrev=False)
    Stm32flashBinaryRunner.add_parser(parser)
    arg_namespace = parser.parse_args(args)
    runner = Stm32flashBinaryRunner.create(runner_config, arg_namespace)
    with patch('os.path.getsize', side_effect=os_path_getsize_patch):
        with patch('os.path.isfile', side_effect=os_path_isfile_patch):
            runner.run('flash')
    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS[action]]
