<filename>tests/test_cli.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Tests for cli.py."""
import os
from unittest import mock

from click.testing import CliRunner
import pytest

from ubittool import cli, cmds


@pytest.fixture
def check_no_board_connected():
    """Check that there is no mbed board that PyOCD can connect to."""
    try:
        cmds._read_continuous_memory(address=0x00, count=16)
    except Exception:
        # Good: Exception raised if no board is found
        pass
    else:
        raise Exception("Found an Mbed device connected, please unplug.")


@mock.patch("ubittool.cli.os.path.exists", autospec=True)
@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker(mock_exit, mock_echo, mock_exists):
    """Test the file checker perform the required checks and prints info."""
    mock_exists.return_value = False

    cli._file_checker("subject", "file/path.py")

    mock_exists.assert_called_once_with("file/path.py")
    assert mock_echo.call_count == 1
    assert "subject will be written to: file/path.py" in mock_echo.call_args[0]
    assert mock_exit.call_count == 0


@mock.patch("ubittool.cli.os.path.exists", autospec=True)
@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker_existing_path(mock_exit, mock_echo, mock_exists):
    """Test file checker exits with error if the file exists."""
    mock_exists.return_value = True

    cli._file_checker("subject", "file/path.py")

    mock_exists.assert_called_once_with("file/path.py")
    assert mock_echo.call_count == 1
    assert (
        "Abort: The file/path.py file already exists."
        in mock_echo.call_args[0][0]
    )
    mock_exit.assert_called_once_with(1)


@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker_no_path(mock_exit, mock_echo):
    """Test the file check informs about console output if no file is given."""
    cli._file_checker("subject", None)

    assert mock_echo.call_count == 1
    assert "subject will be output to console." in mock_echo.call_args[0]
    assert mock_exit.call_count == 0


@mock.patch("ubittool.cli.read_python_code", autospec=True)
def test_read_code(mock_read_python_code, check_no_board_connected):
    """Test the read-code command without a file option."""
    python_code = "Python code here"
    mock_read_python_code.return_value = python_code
    runner = CliRunner()

    result = runner.invoke(cli.read_code)

    assert "MicroPython code will be output to console." in result.output
    assert "Printing the MicroPython code" in result.output
    assert python_code in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_code_no_board(check_no_board_connected):
    """Test the read-code command when no board is connected."""
    runner = CliRunner()

    result = runner.invoke(cli.read_code)

    assert result.exit_code != 0
    assert "MicroPython code will be output to console." in result.output
    assert "Did not find any connected boards." in result.output


@mock.patch("ubittool.cli.read_python_code", autospec=True)
def test_read_code_path(mock_read_python_code, check_no_board_connected):
    """Test the read-code command with a file option."""
    mock_read_python_code.return_value = "Python code here"
    runner = CliRunner()

    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open:
        result = runner.invoke(cli.read_code, ["--file_path", "thisfile.py"])

    m_open.assert_called_once_with("thisfile.py", "w")
    m_open().write.assert_called_once_with("Python code here")
    assert "MicroPython code will be written to: thisfile.py" in result.output
    assert "Saving the MicroPython code..." in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_code_path_no_board(check_no_board_connected):
    """Test read-code command with a file option and no board connected."""
    file_name = "thisfile.py"
    runner = CliRunner()

    results = [
        runner.invoke(cli.read_code, ["--file_path", file_name]),
        runner.invoke(cli.read_code, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert (
            "MicroPython code will be written to: {}".format(file_name)
            in result.output
        ), "Message written to file"
        assert (
            "Did not find any connected boards." in result.output
        ), "Message error, board not found"
    # File not mocked, so checking command hasn't created it
    assert not os.path.isfile(file_name), "File does not exist"


@mock.patch("ubittool.cli.read_flash_hex", autospec=True)
def test_read_flash(mock_read_flash_hex, check_no_board_connected):
    """Test the read-flash command without a file option."""
    flash_hex_content = "Intel Hex lines here"
    mock_read_flash_hex.return_value = flash_hex_content
    runner = CliRunner()

    result = runner.invoke(cli.read_flash)

    assert "micro:bit flash hex will be output to console." in result.output
    assert "Printing the flash contents" in result.output
    assert flash_hex_content in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_flash_no_board(check_no_board_connected):
    """Test the read-flash command when no board is connected."""
    runner = CliRunner()

    result = runner.invoke(cli.read_flash)

    assert result.exit_code != 0
    assert "micro:bit flash hex will be output to console." in result.output
    assert "Did not find any connected boards." in result.output


@mock.patch("ubittool.cli.read_flash_hex", autospec=True)
def test_read_flash_path(mock_read_flash_hex, check_no_board_connected):
    """Test the read-code command with a file option."""
    flash_hex_content = "Intel Hex lines here"
    mock_read_flash_hex.return_value = flash_hex_content
    file_name = "thisfile.py"
    runner = CliRunner()

    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open:
        results = [runner.invoke(cli.read_flash, ["--file_path", file_name])]
    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open2:
        results.append(runner.invoke(cli.read_flash, ["-f", file_name]))

    m_open.assert_called_once_with(file_name, "w")
    m_open2.assert_called_once_with(file_name, "w")
    m_open().write.assert_called_once_with(flash_hex_content)
    m_open2().write.assert_called_once_with(flash_hex_content)
    for result in results:
        assert (
            "micro:bit flash hex will be written to: {}".format(file_name)
            in result.output
        )
        assert "Saving the flash contents..." in result.output
        assert "Finished successfully" in result.output
        assert result.exit_code == 0


def test_read_flash_path_no_board(check_no_board_connected):
    """Test read-flash command with a file option and no board connected."""
    file_name = "thisfile.py"
    runner = CliRunner()

    results = [
        runner.invoke(cli.read_flash, ["--file_path", file_name]),
        runner.invoke(cli.read_flash, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert (
            "micro:bit flash hex will be written to: {}".format(file_name)
            in result.output
        ), "Message written to file"
        assert (
            "Did not find any connected boards." in result.output
        ), "Message error, board not found"
    # File not mocked, so checking command hasn't created it
    assert not os.path.isfile(file_name), "File does not exist"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
@mock.patch("ubittool.cli.compare_full_flash_hex", autospec=True)
def test_compare_flash(mock_compare, mock_isfile, check_no_board_connected):
    """Test the compare-flash command."""
    file_name = "random_file_name.hex"
    mock_isfile.return_value = True
    mock_compare.return_value = 0
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["-f", file_name]),
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
    ]

    assert mock_compare.call_count == len(results)
    for result in results:
        assert "Diff output loaded in the default browser." in result.output
        assert "Finished successfully." in result.output
        assert result.exit_code == 0, "Exit code 0"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
@mock.patch("ubittool.cli.compare_full_flash_hex", autospec=True)
def test_compare_flash_diffs(
    mock_compare, mock_isfile, check_no_board_connected
):
    """Test the compare-flash command."""
    file_name = "random_file_name.hex"
    mock_isfile.return_value = True
    mock_compare.return_value = 1
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["-f", file_name]),
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
    ]

    assert mock_compare.call_count == len(results)
    for result in results:
        assert "Diff output loaded in the default browser." in result.output
        assert (
            "There are some differences in the micro:bit flash!"
            in result.output
        )
        assert result.exit_code != 0, "Exit code non-zero"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
def test_compare_flash_no_board(mock_isfile, check_no_board_connected):
    """Test the compare-flash command when no board is connected."""
    file_name = "random_file_name.hex"
    file_content = "Intel Hex lines here"
    mock_isfile.return_value = True
    runner = CliRunner()

    with mock.patch(
        "ubittool.cmds.open", mock.mock_open(read_data=file_content)
    ) as m_open:
        results = [
            runner.invoke(cli.compare_flash, ["-f", file_name]),
            runner.invoke(cli.compare_flash, ["--file_path", file_name]),
        ]

    assert m_open.call_count == len(results)
    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert "Did not find any connected boards." in result.output


def test_compare_flash_invalid_file():
    """Check error is thrown when compare-flash file does not exist."""
    file_name = "random_file_does_not_exist.hex"
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
        runner.invoke(cli.compare_flash, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert "Abort: File does not exists" in result.output


def test_compare_flash_no_file():
    """Test there is an error when compare-flash doesn't have a file arg."""
    runner = CliRunner()

    result = runner.invoke(cli.compare_flash)

    assert result.exit_code != 0, "Exit code non-zero"
    assert "Error: Missing option '-f' / '--file_path'." in result.output


@mock.patch("ubittool.gui.open_gui", autospec=True)
def test_gui(mock_open_gui, check_no_board_connected):
    """Test the gui command."""
    runner = CliRunner()

    result = runner.invoke(cli.gui)

    assert result.exit_code == 0, "Exit code 0"
    assert mock_open_gui.call_count == 1, "open_gui() function called"

<filename>tests/test_cli.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Tests for cli.py."""
import os
from unittest import mock

from click.testing import CliRunner
import pytest

from ubittool import cli, cmds


@pytest.fixture
def check_no_board_connected():
    """Check that there is no mbed board that PyOCD can connect to."""
    try:
        cmds._read_continuous_memory(address=0x00, count=16)
    except Exception:
        # Good: Exception raised if no board is found
        pass
    else:
        raise Exception("Found an Mbed device connected, please unplug.")


@mock.patch("ubittool.cli.os.path.exists", autospec=True)
@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker(mock_exit, mock_echo, mock_exists):
    """Test the file checker perform the required checks and prints info."""
    mock_exists.return_value = False

    cli._file_checker("subject", "file/path.py")

    mock_exists.assert_called_once_with("file/path.py")
    assert mock_echo.call_count == 1
    assert "subject will be written to: file/path.py" in mock_echo.call_args[0]
    assert mock_exit.call_count == 0


@mock.patch("ubittool.cli.os.path.exists", autospec=True)
@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker_existing_path(mock_exit, mock_echo, mock_exists):
    """Test file checker exits with error if the file exists."""
    mock_exists.return_value = True

    cli._file_checker("subject", "file/path.py")

    mock_exists.assert_called_once_with("file/path.py")
    assert mock_echo.call_count == 1
    assert (
        "Abort: The file/path.py file already exists."
        in mock_echo.call_args[0][0]
    )
    mock_exit.assert_called_once_with(1)


@mock.patch("ubittool.cli.click.echo", autospec=True)
@mock.patch("ubittool.cli.sys.exit", autospec=True)
def test_file_checker_no_path(mock_exit, mock_echo):
    """Test the file check informs about console output if no file is given."""
    cli._file_checker("subject", None)

    assert mock_echo.call_count == 1
    assert "subject will be output to console." in mock_echo.call_args[0]
    assert mock_exit.call_count == 0


@mock.patch("ubittool.cli.read_python_code", autospec=True)
def test_read_code(mock_read_python_code, check_no_board_connected):
    """Test the read-code command without a file option."""
    python_code = "Python code here"
    mock_read_python_code.return_value = python_code
    runner = CliRunner()

    result = runner.invoke(cli.read_code)

    assert "MicroPython code will be output to console." in result.output
    assert "Printing the MicroPython code" in result.output
    assert python_code in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_code_no_board(check_no_board_connected):
    """Test the read-code command when no board is connected."""
    runner = CliRunner()

    result = runner.invoke(cli.read_code)

    assert result.exit_code != 0
    assert "MicroPython code will be output to console." in result.output
    assert "Did not find any connected boards." in result.output


@mock.patch("ubittool.cli.read_python_code", autospec=True)
def test_read_code_path(mock_read_python_code, check_no_board_connected):
    """Test the read-code command with a file option."""
    mock_read_python_code.return_value = "Python code here"
    runner = CliRunner()

    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open:
        result = runner.invoke(cli.read_code, ["--file_path", "thisfile.py"])

    m_open.assert_called_once_with("thisfile.py", "w")
    m_open().write.assert_called_once_with("Python code here")
    assert "MicroPython code will be written to: thisfile.py" in result.output
    assert "Saving the MicroPython code..." in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_code_path_no_board(check_no_board_connected):
    """Test read-code command with a file option and no board connected."""
    file_name = "thisfile.py"
    runner = CliRunner()

    results = [
        runner.invoke(cli.read_code, ["--file_path", file_name]),
        runner.invoke(cli.read_code, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert (
            "MicroPython code will be written to: {}".format(file_name)
            in result.output
        ), "Message written to file"
        assert (
            "Did not find any connected boards." in result.output
        ), "Message error, board not found"
    # File not mocked, so checking command hasn't created it
    assert not os.path.isfile(file_name), "File does not exist"


@mock.patch("ubittool.cli.read_flash_hex", autospec=True)
def test_read_flash(mock_read_flash_hex, check_no_board_connected):
    """Test the read-flash command without a file option."""
    flash_hex_content = "Intel Hex lines here"
    mock_read_flash_hex.return_value = flash_hex_content
    runner = CliRunner()

    result = runner.invoke(cli.read_flash)

    assert "micro:bit flash hex will be output to console." in result.output
    assert "Printing the flash contents" in result.output
    assert flash_hex_content in result.output
    assert "Finished successfully" in result.output
    assert result.exit_code == 0


def test_read_flash_no_board(check_no_board_connected):
    """Test the read-flash command when no board is connected."""
    runner = CliRunner()

    result = runner.invoke(cli.read_flash)

    assert result.exit_code != 0
    assert "micro:bit flash hex will be output to console." in result.output
    assert "Did not find any connected boards." in result.output


@mock.patch("ubittool.cli.read_flash_hex", autospec=True)
def test_read_flash_path(mock_read_flash_hex, check_no_board_connected):
    """Test the read-code command with a file option."""
    flash_hex_content = "Intel Hex lines here"
    mock_read_flash_hex.return_value = flash_hex_content
    file_name = "thisfile.py"
    runner = CliRunner()

    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open:
        results = [runner.invoke(cli.read_flash, ["--file_path", file_name])]
    with mock.patch("ubittool.cli.open", mock.mock_open()) as m_open2:
        results.append(runner.invoke(cli.read_flash, ["-f", file_name]))

    m_open.assert_called_once_with(file_name, "w")
    m_open2.assert_called_once_with(file_name, "w")
    m_open().write.assert_called_once_with(flash_hex_content)
    m_open2().write.assert_called_once_with(flash_hex_content)
    for result in results:
        assert (
            "micro:bit flash hex will be written to: {}".format(file_name)
            in result.output
        )
        assert "Saving the flash contents..." in result.output
        assert "Finished successfully" in result.output
        assert result.exit_code == 0


def test_read_flash_path_no_board(check_no_board_connected):
    """Test read-flash command with a file option and no board connected."""
    file_name = "thisfile.py"
    runner = CliRunner()

    results = [
        runner.invoke(cli.read_flash, ["--file_path", file_name]),
        runner.invoke(cli.read_flash, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert (
            "micro:bit flash hex will be written to: {}".format(file_name)
            in result.output
        ), "Message written to file"
        assert (
            "Did not find any connected boards." in result.output
        ), "Message error, board not found"
    # File not mocked, so checking command hasn't created it
    assert not os.path.isfile(file_name), "File does not exist"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
@mock.patch("ubittool.cli.compare_full_flash_hex", autospec=True)
def test_compare_flash(mock_compare, mock_isfile, check_no_board_connected):
    """Test the compare-flash command."""
    file_name = "random_file_name.hex"
    mock_isfile.return_value = True
    mock_compare.return_value = 0
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["-f", file_name]),
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
    ]

    assert mock_compare.call_count == len(results)
    for result in results:
        assert "Diff output loaded in the default browser." in result.output
        assert "Finished successfully." in result.output
        assert result.exit_code == 0, "Exit code 0"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
@mock.patch("ubittool.cli.compare_full_flash_hex", autospec=True)
def test_compare_flash_diffs(
    mock_compare, mock_isfile, check_no_board_connected
):
    """Test the compare-flash command."""
    file_name = "random_file_name.hex"
    mock_isfile.return_value = True
    mock_compare.return_value = 1
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["-f", file_name]),
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
    ]

    assert mock_compare.call_count == len(results)
    for result in results:
        assert "Diff output loaded in the default browser." in result.output
        assert (
            "There are some differences in the micro:bit flash!"
            in result.output
        )
        assert result.exit_code != 0, "Exit code non-zero"


@mock.patch("ubittool.cli.os.path.isfile", autospec=True)
def test_compare_flash_no_board(mock_isfile, check_no_board_connected):
    """Test the compare-flash command when no board is connected."""
    file_name = "random_file_name.hex"
    file_content = "Intel Hex lines here"
    mock_isfile.return_value = True
    runner = CliRunner()

    with mock.patch(
        "ubittool.cmds.open", mock.mock_open(read_data=file_content)
    ) as m_open:
        results = [
            runner.invoke(cli.compare_flash, ["-f", file_name]),
            runner.invoke(cli.compare_flash, ["--file_path", file_name]),
        ]

    assert m_open.call_count == len(results)
    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert "Did not find any connected boards." in result.output


def test_compare_flash_invalid_file():
    """Check error is thrown when compare-flash file does not exist."""
    file_name = "random_file_does_not_exist.hex"
    runner = CliRunner()

    results = [
        runner.invoke(cli.compare_flash, ["--file_path", file_name]),
        runner.invoke(cli.compare_flash, ["-f", file_name]),
    ]

    for result in results:
        assert result.exit_code != 0, "Exit code non-zero"
        assert "Abort: File does not exists" in result.output


def test_compare_flash_no_file():
    """Test there is an error when compare-flash doesn't have a file arg."""
    runner = CliRunner()

    result = runner.invoke(cli.compare_flash)

    assert result.exit_code != 0, "Exit code non-zero"
    assert "Error: Missing option '-f' / '--file_path'." in result.output


@mock.patch("ubittool.gui.open_gui", autospec=True)
def test_gui(mock_open_gui, check_no_board_connected):
    """Test the gui command."""
    runner = CliRunner()

    result = runner.invoke(cli.gui)

    assert result.exit_code == 0, "Exit code 0"
    assert mock_open_gui.call_count == 1, "open_gui() function called"

#!/usr/bin/env python3 # -*- coding: utf-8 -*- Tests for cli.py. Check that there is no mbed board that PyOCD can connect to. # Good: Exception raised if no board is found Test the file checker perform the required checks and prints info. Test file checker exits with error if the file exists. Test the file check informs about console output if no file is given. Test the read-code command without a file option. Test the read-code command when no board is connected. Test the read-code command with a file option. Test read-code command with a file option and no board connected. # File not mocked, so checking command hasn't created it Test the read-flash command without a file option. Test the read-flash command when no board is connected. Test the read-code command with a file option. Test read-flash command with a file option and no board connected. # File not mocked, so checking command hasn't created it Test the compare-flash command. Test the compare-flash command. Test the compare-flash command when no board is connected. Check error is thrown when compare-flash file does not exist. Test there is an error when compare-flash doesn't have a file arg. Test the gui command.