Newer
Older
mbed-os / tools / test / toolchains / api_test.py
@Cruz Monrreal II Cruz Monrreal II on 17 Jan 2019 12 KB Added warning suppression to python api_tests.
"""Tests for the toolchain sub-system"""
import sys
import os
from string import printable
from copy import deepcopy
from mock import MagicMock, patch
from hypothesis import given, settings, HealthCheck
from hypothesis.strategies import text, lists, fixed_dictionaries, booleans

ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..",
                                    ".."))
sys.path.insert(0, ROOT)

from tools.toolchains import (
    TOOLCHAIN_CLASSES,
    TOOLCHAIN_PATHS,
    mbedToolchain,
)
from tools.resources import LEGACY_TOOLCHAIN_NAMES, Resources, FileType
from tools.targets import TARGET_MAP, set_targets_json_location
from tools.notifier.mock import MockNotifier

ALPHABET = [char for char in printable if char not in [u'.', u'/', u'\\']]


@patch('tools.toolchains.arm.run_cmd')
def test_arm_version_check(_run_cmd):
    set_targets_json_location()
    _run_cmd.return_value = ("""
    Product: ARM Compiler 5.06
    Component: ARM Compiler 5.06 update 5 (build 528)
    Tool: armcc [4d3621]
    """, "", 0)
    notifier = MockNotifier()
    toolchain = TOOLCHAIN_CLASSES["ARM"](TARGET_MAP["K64F"], notify=notifier)
    toolchain.version_check()
    assert notifier.messages == []
    _run_cmd.return_value = ("""
    Product: MDK Professional 5.22
    Component: ARM Compiler 5.06 update 5 (build 528)
    Tool: armcc [4d3621]
    """, "", 0)
    toolchain.version_check()
    assert notifier.messages == []
    _run_cmd.return_value = ("""
    Product: ARM Compiler
    Component: ARM Compiler
    Tool: armcc [4d3621]
    """, "", 0)
    toolchain.version_check()
    assert len(notifier.messages) == 1


@patch('tools.toolchains.iar.run_cmd')
def test_iar_version_check(_run_cmd):
    set_targets_json_location()
    _run_cmd.return_value = ("""
    IAR ANSI C/C++ Compiler V7.80.1.28/LNX for ARM
    """, "", 0)
    notifier = MockNotifier()
    toolchain = TOOLCHAIN_CLASSES["IAR"](TARGET_MAP["K64F"], notify=notifier)
    toolchain.version_check()
    assert notifier.messages == []
    _run_cmd.return_value = ("""
    IAR ANSI C/C++ Compiler V/LNX for ARM
    """, "", 0)
    toolchain.version_check()
    assert len(notifier.messages) == 1
    _run_cmd.return_value = ("""
    IAR ANSI C/C++ Compiler V/8.80LNX for ARM
    """, "", 0)
    toolchain.version_check()
    assert len(notifier.messages) == 2


@patch('tools.toolchains.gcc.run_cmd')
def test_gcc_version_check(_run_cmd):
    set_targets_json_location()
    _run_cmd.return_value = ("""
    arm-none-eabi-gcc (Arch Repository) 6.4.4
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    """, "", 0)
    notifier = MockNotifier()
    toolchain = TOOLCHAIN_CLASSES["GCC_ARM"](
        TARGET_MAP["K64F"], notify=notifier)
    toolchain.version_check()
    assert notifier.messages == []
    _run_cmd.return_value = ("""
    arm-none-eabi-gcc (Arch Repository) 8.1.0
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    """, "", 0)
    toolchain.version_check()
    assert len(notifier.messages) == 1
    _run_cmd.return_value = ("""
    arm-none-eabi-gcc (Arch Repository)
    Copyright (C) 2018 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    """, "", 0)
    toolchain.version_check()
    assert len(notifier.messages) == 2


@given(fixed_dictionaries({
    'common': lists(text()),
    'c': lists(text()),
    'cxx': lists(text()),
    'asm': lists(text()),
    'ld': lists(text())}),
       lists(text(min_size=1, alphabet=ALPHABET), min_size=1))
@settings(suppress_health_check=[HealthCheck.too_slow])
def test_toolchain_profile_c(profile, source_file):
    """Test that the appropriate profile parameters are passed to the
    C compiler"""
    filename = deepcopy(source_file)
    filename[-1] += ".c"
    to_compile = os.path.join(*filename)
    set_targets_json_location()
    with patch('os.mkdir') as _mkdir:
        for _, tc_class in TOOLCHAIN_CLASSES.items():
            toolchain = tc_class(TARGET_MAP["K64F"], build_profile=profile,
                                 notify=MockNotifier())
            toolchain.inc_md5 = ""
            toolchain.build_dir = ""
            toolchain.config = MagicMock(app_config_location=None)
            for parameter in profile['c'] + profile['common']:
                assert any(parameter in cmd for cmd in toolchain.cc), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                            parameter)
            compile_command = toolchain.compile_command(to_compile,
                                                        to_compile + ".o", [])
            for parameter in profile['c'] + profile['common']:
                assert any(parameter in cmd for cmd in compile_command), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                            parameter)

@given(fixed_dictionaries({
    'common': lists(text()),
    'c': lists(text()),
    'cxx': lists(text()),
    'asm': lists(text()),
    'ld': lists(text())}),
       lists(text(min_size=1, alphabet=ALPHABET), min_size=1))
@settings(suppress_health_check=[HealthCheck.too_slow])
def test_toolchain_profile_cpp(profile, source_file):
    """Test that the appropriate profile parameters are passed to the
    C++ compiler"""
    filename = deepcopy(source_file)
    filename[-1] += ".cpp"
    to_compile = os.path.join(*filename)
    with patch('os.mkdir') as _mkdir:
        for _, tc_class in TOOLCHAIN_CLASSES.items():
            toolchain = tc_class(TARGET_MAP["K64F"], build_profile=profile,
                                 notify=MockNotifier())
            toolchain.inc_md5 = ""
            toolchain.build_dir = ""
            toolchain.config = MagicMock(app_config_location=None)
            for parameter in profile['cxx'] + profile['common']:
                assert any(parameter in cmd for cmd in toolchain.cppc), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                            parameter)
            compile_command = toolchain.compile_command(to_compile,
                                                        to_compile + ".o", [])
            for parameter in profile['cxx'] + profile['common']:
                assert any(parameter in cmd for cmd in compile_command), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                            parameter)

@given(fixed_dictionaries({
    'common': lists(text()),
    'c': lists(text()),
    'cxx': lists(text()),
    'asm': lists(text()),
    'ld': lists(text())}),
       lists(text(min_size=1, alphabet=ALPHABET), min_size=1))
@settings(suppress_health_check=[HealthCheck.too_slow])
def test_toolchain_profile_asm(profile, source_file):
    """Test that the appropriate profile parameters are passed to the
    Assembler"""
    filename = deepcopy(source_file)
    filename[-1] += ".s"
    to_compile = os.path.join(*filename)
    with patch('os.mkdir') as _mkdir:
        for _, tc_class in TOOLCHAIN_CLASSES.items():
            toolchain = tc_class(TARGET_MAP["K64F"], build_profile=profile,
                                 notify=MockNotifier())
            toolchain.inc_md5 = ""
            toolchain.build_dir = ""
            toolchain.config = MagicMock()
            toolchain.config.get_config_data_macros.return_value = []
            for parameter in profile['asm']:
                assert any(parameter in cmd for cmd in toolchain.asm), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                               parameter)
            compile_command = toolchain.compile_command(to_compile,
                                                        to_compile + ".o", [])
            if not compile_command:
                assert compile_command, to_compile
            for parameter in profile['asm']:
                assert any(parameter in cmd for cmd in compile_command), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                               parameter)

    for name, Class in  TOOLCHAIN_CLASSES.items():
        CLS = Class(TARGET_MAP["K64F"], notify=MockNotifier())
        assert name == CLS.name or name ==  LEGACY_TOOLCHAIN_NAMES[CLS.name]

@given(fixed_dictionaries({
    'common': lists(text()),
    'c': lists(text()),
    'cxx': lists(text()),
    'asm': lists(text()),
    'ld': lists(text(min_size=1))}),
       lists(text(min_size=1, alphabet=ALPHABET), min_size=1))
@settings(suppress_health_check=[HealthCheck.too_slow])
def test_toolchain_profile_ld(profile, source_file):
    """Test that the appropriate profile parameters are passed to the
    Linker"""
    filename = deepcopy(source_file)
    filename[-1] += ".o"
    to_compile = os.path.join(*filename)
    with patch('os.mkdir') as _mkdir,\
         patch('tools.toolchains.mbedToolchain.default_cmd') as _dflt_cmd:
        for _, tc_class in TOOLCHAIN_CLASSES.items():
            toolchain = tc_class(TARGET_MAP["K64F"], build_profile=profile,
                                 notify=MockNotifier())
            toolchain.RESPONSE_FILES = False
            toolchain.inc_md5 = ""
            toolchain.build_dir = ""
            for parameter in profile['ld']:
                assert any(parameter in cmd for cmd in toolchain.ld), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                               parameter)
            toolchain.link(to_compile + ".elf", [to_compile], [], [], None)
            compile_cmd = _dflt_cmd.call_args_list
            if not compile_cmd:
                assert compile_cmd, to_compile
            for parameter in profile['ld']:
                assert any(parameter in cmd[0][0] for cmd in compile_cmd), \
                    "Toolchain %s did not propagate arg %s" % (toolchain.name,
                                                               parameter)

    for name, Class in  TOOLCHAIN_CLASSES.items():
        CLS = Class(TARGET_MAP["K64F"], notify=MockNotifier())
        assert name == CLS.name or name ==  LEGACY_TOOLCHAIN_NAMES[CLS.name]


@given(lists(text(alphabet=ALPHABET, min_size=1), min_size=1))
def test_detect_duplicates(filenames):
    c_sources = [os.path.join(name, "dupe.c") for name in filenames]
    s_sources = [os.path.join(name, "dupe.s") for name in filenames]
    cpp_sources = [os.path.join(name, "dupe.cpp") for name in filenames]
    notify = MockNotifier()
    res = Resources(notify)
    res.add_files_to_type(FileType.C_SRC, c_sources)
    res.add_files_to_type(FileType.ASM_SRC, s_sources)
    res.add_files_to_type(FileType.CPP_SRC, cpp_sources)
    assert res.detect_duplicates() == 1,\
        "Not Enough duplicates found"

    notification = notify.messages[0]
    assert "dupe.o" in notification["message"]
    assert "dupe.s" in notification["message"]
    assert "dupe.c" in notification["message"]
    assert "dupe.cpp" in notification["message"]

@given(text(alphabet=ALPHABET + [os.sep], min_size=1))
@given(booleans())
@given(booleans())
@settings(max_examples=20)
def test_path_specified_gcc(gcc_loc, exists_at_loc, exists_in_path):
    with patch('tools.toolchains.gcc.exists') as _exists:
        with patch('tools.toolchains.gcc.find_executable') as _find:
            _exists.return_value = exists_at_loc
            _find.return_value = exists_in_path
            TOOLCHAIN_PATHS['GCC_ARM'] = gcc_loc
            toolchain_class = TOOLCHAIN_CLASSES["GCC_ARM"]
            found_p = toolchain_class.check_executable()
            assert found_p == (exists_at_loc or exists_in_path)
            if exists_at_loc:
                assert TOOLCHAIN_PATHS['GCC_ARM'] == gcc_loc
            elif exists_in_path:
                assert TOOLCHAIN_PATHS['GCC_ARM'] == ''