Skip to content
Snippets Groups Projects
test_build.py 22.4 KiB
Newer Older
import os
import re
from pathlib import Path
import pytest

from .utils import Wrapper, run_ioc_get_output
Simon Rose's avatar
Simon Rose committed

RE_MISSING_FILE = "No rule to make target [`']{filename}'"
RE_MISSING_VERSION = "Module '{module}' version '{version}' does not exist."
RE_MODULE_VERSION_EXISTS = "Error .*{module}/{version}.* already exists"
Simon Rose's avatar
Simon Rose committed

def create_patch_file(path, desc):
    path.parent.mkdir(parents=True, exist_ok=True)
    patch_file_contents = """
Simon Rose's avatar
Simon Rose committed
diff --git database.db database.db
index 1806ff6..8701832 100644
--- database.db
+++ database.db
@@ -1,3 +1,3 @@
 record(ai, "TEST") {{
Simon Rose's avatar
Simon Rose committed
-
Simon Rose's avatar
Simon Rose committed
+    field(DESC, "{desc}")
 }}
"""
Simon Rose's avatar
Simon Rose committed
    with open(path, "w") as f:
        f.write(patch_file_contents.format(desc=desc))
def test_patch(wrapper: Wrapper):
    db_path = wrapper.module_dir / "database.db"
    db_file_contents = """record(ai, "TEST") {

}
"""
Simon Rose's avatar
Simon Rose committed
    with open(db_path, "w") as f:
        f.write(db_file_contents)
Simon Rose's avatar
Simon Rose committed
    patch_dir = wrapper.path / "patch"
    create_patch_file(patch_dir / "apply.p0.patch", "OK")
    create_patch_file(patch_dir / wrapper.version / "do-not-apply.p0.patch", "Bad")
    rc, _, _ = wrapper.run_make("patch")
Simon Rose's avatar
Simon Rose committed
    assert rc == 0
Simon Rose's avatar
Simon Rose committed
    with open(db_path, "r") as f:
Simon Rose's avatar
Simon Rose committed
        db_contents = f.read()
    assert 'field(DESC, "OK")' in db_contents
Simon Rose's avatar
Simon Rose committed
    assert "Bad" not in db_contents
    rc, _, _ = wrapper.run_make("build")
Simon Rose's avatar
Simon Rose committed
    assert rc == 0
    assert any((wrapper.module_dir).glob("O.*"))
    rc, _, _ = wrapper.run_make("cellinstall")
    assert any((wrapper.path / "cellMods").glob("**/*.db"))
def test_local_module(wrapper: Wrapper):
    rc, outs, _ = wrapper.run_make("init")
    assert rc == 0
    assert "You are in the local source mode" in outs


def test_missing_dbd_file(wrapper: Wrapper):
    wrapper.add_var_to_module_makefile("DBDS", "nonexistent.dbd")
    rc, _, errs = wrapper.run_make("build")
Simon Rose's avatar
Simon Rose committed

    assert re.search(
        RE_MISSING_FILE.format(filename=re.escape("../nonexistent.dbd")),
def test_missing_source_file(wrapper: Wrapper):
    wrapper.add_var_to_module_makefile("SOURCES", "nonexistent.c")
    rc, _, errs = wrapper.run_make("build")

    assert rc == 2
    assert re.search(
        RE_MISSING_FILE.format(filename=re.escape("nonexistent.o")),
def test_missing_requirement(wrapper: Wrapper):
    missing_module = "FOO"
    wrapper.add_var_to_config_module(f"{missing_module}_DEP_VERSION", "bar")
    rc, _, errs = wrapper.run_make("build")
    assert rc != 0
    print(errs)
    assert (
        f'Dependent module "{missing_module.lower()}" (from "{missing_module}_DEP_VERSION") not installed'
        in errs
    )
def test_missing_dependent_version(wrappers):
    wrapper_dep = wrappers.get()
    wrapper_main = wrappers.get()

    cell_path = wrapper_main.path / "cellMods"

    rc, *_ = wrapper_dep.run_make("cellinstall", cell_path=cell_path)
    assert rc == 0

    missing_version = "not_a_real_version"
    wrapper_main.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", missing_version
    )
    rc, _, errs = wrapper_main.run_make("cellbuild")
    assert rc == 2
    assert re.search(
        RE_MISSING_VERSION.format(module=wrapper_dep.name, version=missing_version),
        errs,
    )


def test_header_install_location(wrapper: Wrapper):
    subdir = wrapper.module_dir / "db" / "subdir"
    subdir.mkdir(parents=True)

    extensions = ["h", "hpp", "hxx", "hh"]
    for ext in extensions:
        wrapper.add_var_to_module_makefile("HEADERS", f"db/subdir/header.{ext}")
    wrapper.add_var_to_module_makefile("KEEP_HEADER_SUBDIRS", "db")
    for ext in extensions:
        (subdir / f"header.{ext}").touch()
    rc, *_ = wrapper.run_make("cellinstall")
    assert rc == 0

    cell_path = wrapper.get_env_var("E3_MODULES_INSTALL_LOCATION")

    for ext in extensions:
        assert (Path(cell_path) / "include" / "subdir" / f"header.{ext}").is_file()
        assert not (Path(cell_path) / "include" / f"header.{ext}").is_file()


def test_updated_dependencies(wrappers):
    wrapper_dep = wrappers.get()
    wrapper_main = wrappers.get()

    cell_path = wrapper_main.path / "cellMods"

    old_version = "0.0.0+0"

Simon Rose's avatar
Simon Rose committed
    wrapper_main.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", old_version, modifier=""
    rc, *_ = wrapper_dep.run_make(
        "cellinstall",
        module_version=old_version,
        cell_path=cell_path,
    )
    assert rc == 0

    rc, *_ = wrapper_main.run_make("cellinstall", module_version=old_version)
    assert rc == 0

    new_version = "1.0.0+0"

    rc, *_ = wrapper_dep.run_make(
        "cellinstall",
        module_version=new_version,
        cell_path=cell_path,
    )
    assert rc == 0

Simon Rose's avatar
Simon Rose committed
    wrapper_main.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", new_version, modifier=""
    rc, *_ = wrapper_main.run_make("cellinstall", module_version=new_version)
    assert rc == 0

    rc, outs, _ = run_ioc_get_output(
        module=wrapper_main.name,
        version=new_version,
        cell_path=wrapper_main.path / "cellMods",
    )
    assert rc == 0
    assert f"Loaded {wrapper_dep.name} version {new_version}" in outs
def test_match_versions(wrappers):
    """Test match version scenario.

    This test checks if inconsistent versions are correctly verified during
    build time. This tests if the dependecies B->A->C and B->C with A and B
    both requesting the same version of C will be correctly built.
    """
    wrapper_dep = wrappers.get()
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()

    cell_path = wrapper_b.path / "cellmods"

    dep_version = "1.0.0+0"

    a_version = "0.0.0+0"
    b_version = "0.0.0+0"

    # Wrapper a dependes on dep,1.0.0+0
    wrapper_a.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", dep_version, modifier=""
    )

    # Wrapper b depends on dep,1.0.0+0
    wrapper_b.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", dep_version, modifier=""
    )

    # Wrapper b also depends on a
    wrapper_b.add_var_to_config_module(
        f"{wrapper_a.name}_DEP_VERSION", a_version, modifier=""
    )

    rc, *_ = wrapper_dep.run_make(
        "cellinstall", module_version=dep_version, cell_path=cell_path
    )
    assert rc == 0

    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=a_version, cell_path=cell_path
    )
    assert rc == 0

    # As wrappers a and b both depends on dep,1.0.0+0 this build should finish
    # corretly.
    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=b_version, cell_path=cell_path
    )
    assert rc == 0


def test_unmatching_versions(wrappers):
    """Test unmatching version scenarion.

    This test checks if inconsistent versions are correctly verified during
    build time. This checks for the scenarion where B->A->C and B->C however
    A depends on a version of C different than B.
    """
    wrapper_dep = wrappers.get()
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()

    cell_path = wrapper_b.path / "cellmods"

    dep_version_1 = "1.0.0+0"
    dep_version_2 = "2.0.0+0"

    a_version = "0.0.0+0"
    b_version = "0.0.0+0"

    # Wrapper a dependes on dep v1
    wrapper_a.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", dep_version_1, modifier=""
    )

    # Wrapper b depends on dep v2
    wrapper_b.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", dep_version_2, modifier=""
    )

    # Wrapper b also depends on wrapper_a
    wrapper_b.add_var_to_config_module(
        f"{wrapper_a.name}_DEP_VERSION", a_version, modifier=""
    )

    rc, *_ = wrapper_dep.run_make(
        "cellinstall", module_version=dep_version_1, cell_path=cell_path
    )
    assert rc == 0

    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=a_version, cell_path=cell_path
    )
    assert rc == 0

    # Now a second installation of wrapper_dep but with version 2
    rc, *_ = wrapper_dep.run_make(
        "cellinstall", module_version=dep_version_2, cell_path=cell_path
    )
    assert rc == 0

    # This next installation should fail because B depends on A
    # that depends on DEP. However A needs DEP 1.0.0+0 and B
    # needs DEP 2.0.0+0
    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=b_version, cell_path=cell_path
    )
    assert rc != 0


def test_indirect_unmatching_versions(wrappers):
    """Test indirect unmatching version scenarion.

    This test checks if inconsistend versions are correctly verified during
    build time. This checks for the scenarion where B->A->C and B->D->C
    however A depends on a version of C different than D.
    """

    wrapper_c = wrappers.get()
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()
    wrapper_d = wrappers.get()

    cell_path = wrapper_b.path / "cellmods"

    c_version_a = "1.0.0+0"
    c_version_d = "2.0.0+0"

    a_version = "0.0.0+0"
    d_version = "0.0.0+0"
    b_version = "0.0.0+0"

    # Wrapper a dependes on c
    wrapper_a.add_var_to_config_module(
        f"{wrapper_c.name}_DEP_VERSION", c_version_a, modifier=""
    )

    # Wrapper d dependes on c
    wrapper_d.add_var_to_config_module(
        f"{wrapper_c.name}_DEP_VERSION", c_version_d, modifier=""
    )

    # Wrapper b depends on d
    wrapper_b.add_var_to_config_module(
        f"{wrapper_d.name}_DEP_VERSION", d_version, modifier=""
    )

    # Wrapper b also depends on a
    wrapper_b.add_var_to_config_module(
        f"{wrapper_a.name}_DEP_VERSION", a_version, modifier=""
    )

    rc, *_ = wrapper_c.run_make(
        "cellinstall", module_version=c_version_a, cell_path=cell_path
    )
    assert rc == 0

    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=a_version, cell_path=cell_path
    )
    assert rc == 0

    # Now a second installation of wrapper_dep but with version 2
    rc, *_ = wrapper_c.run_make(
        "cellinstall", module_version=c_version_d, cell_path=cell_path
    )
    assert rc == 0

    rc, *_ = wrapper_d.run_make(
        "cellinstall", module_version=d_version, cell_path=cell_path
    )
    assert rc == 0

    # This next installation should fail because A depends on C
    # with a different version that D depends on C.
    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=b_version, cell_path=cell_path
    )
    assert rc != 0


def test_automated_dependency(wrappers):
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()

    cell_path = wrapper_a.path / "cellMods"

    module_version = "0.0.0+0"

    wrapper_a.add_var_to_config_module(f"{wrapper_b.name}_DEP_VERSION", module_version)

    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0
    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0

    for dep_file in (cell_path / wrapper_a.name).rglob("*.dep"):
        with open(dep_file, "r") as f:
            contents = f.readlines()

        assert len(contents) == 2
Simon Rose's avatar
Simon Rose committed
        assert contents[0].strip() == "# Generated file. Do not edit."
        assert f"{wrapper_b.name} {module_version}" == contents[1]


Simon Rose's avatar
Simon Rose committed
def test_architecture_dependent_dependency(wrappers):
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()
    wrapper_c = wrappers.get()

    cell_path = wrapper_a.path / "cellMods"

    module_version = "0.0.0+0"

    wrapper_a.add_var_to_config_module(
        f"{wrapper_b.name}_DEP_VERSION_linux", module_version
    )
    wrapper_a.add_var_to_config_module(
        f"{wrapper_c.name}_DEP_VERSION_not_an_arch", module_version
    )

    rc, *_ = wrapper_c.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0
    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0
    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0

    rc, outs, _ = run_ioc_get_output(
        module=wrapper_a.name,
        version=module_version,
        cell_path=wrapper_a.path / "cellMods",
Simon Rose's avatar
Simon Rose committed
    )
    assert rc == 0
    assert f"Loaded {wrapper_b.name} version {module_version}" in outs
    assert f"Loaded {wrapper_c.name} version {module_version}" not in outs


def test_recursive_header_include(wrappers):
Simon Rose's avatar
Simon Rose committed
    wrapper_a = wrappers.get()
    wrapper_b = wrappers.get()
    wrapper_c = wrappers.get()
    wrapper_d = wrappers.get()
Simon Rose's avatar
Simon Rose committed

    cell_path = wrapper_a.path / "cellMods"

    module_version = "0.0.0+0"

    wrapper_c.add_var_to_config_module(f"{wrapper_d.name}_DEP_VERSION", module_version)
    wrapper_b.add_var_to_config_module(f"{wrapper_c.name}_DEP_VERSION", module_version)
    wrapper_a.add_var_to_config_module(f"{wrapper_b.name}_DEP_VERSION", module_version)
Simon Rose's avatar
Simon Rose committed

    wrapper_d.add_var_to_module_makefile("HEADERS", f"{wrapper_d.name}.h")
    (wrapper_d.module_dir / f"{wrapper_d.name}.h").touch()
Simon Rose's avatar
Simon Rose committed

    wrapper_a.add_var_to_module_makefile("SOURCES", f"{wrapper_a.name}.c")
Simon Rose's avatar
Simon Rose committed
    with open(wrapper_a.module_dir / f"{wrapper_a.name}.c", "w") as f:
        f.write(f'#include "{wrapper_d.name}.h"')
Simon Rose's avatar
Simon Rose committed

    rc, *_ = wrapper_d.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0
    rc, *_ = wrapper_c.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
    assert rc == 0
    rc, *_ = wrapper_b.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
Simon Rose's avatar
Simon Rose committed
    assert rc == 0
    rc, *_ = wrapper_a.run_make(
        "cellinstall", module_version=module_version, cell_path=cell_path
    )
Simon Rose's avatar
Simon Rose committed
    assert rc == 0

    rc, outs, _ = run_ioc_get_output(
        module=wrapper_a.name,
        version=module_version,
        cell_path=wrapper_a.path / "cellMods",
Simon Rose's avatar
Simon Rose committed
    assert rc == 0
    assert f"Loaded {wrapper_c.name} version {module_version}" in outs
def test_updated_template_files(wrapper: Wrapper):
    wrapper.add_var_to_module_makefile("SUBS", "x.substitutions")

    substitution_file = wrapper.module_dir / "x.substitutions"
    substitution_file.write_text("file x.template {pattern {x} {y}}")

    template_file = wrapper.module_dir / "x.template"
Simon Rose's avatar
Simon Rose committed
    template_file.write_text("record(ai, initial) {}")

    base_version = wrapper.get_env_var("EPICS_VERSION_NUMBER")
    db_file = wrapper.module_dir / f"O.{base_version}_Common" / "x.db"

    rc, *_ = wrapper.run_make("db_internal")
    assert rc == 0
Simon Rose's avatar
Simon Rose committed
    assert db_file.read_text() == "record(ai, initial) {}"
Simon Rose's avatar
Simon Rose committed
    template_file.write_text("record(ai, updated) {}")

    rc, *_ = wrapper.run_make("db_internal")
    assert rc == 0
Simon Rose's avatar
Simon Rose committed
    assert db_file.read_text() == "record(ai, updated) {}"
def test_expand_db_files(wrapper: Wrapper):
    """Test that the automated template/substitution file expansion works."""

    wrapper.add_var_to_module_makefile("TMPS", "templates/a.template")
    wrapper.add_var_to_module_makefile("SUBS", "b.substitutions")
    wrapper.add_var_to_module_makefile("USR_DBFLAGS", "-I templates")

    template_dir = wrapper.module_dir / "templates"
    template_dir.mkdir()
    template_file = template_dir / "a.template"
    template_file_contents = "record (ai, $(P)) {}"
    template_file.write_text(template_file_contents)

    substitution_file = wrapper.module_dir / "b.substitutions"
    substitution_file.write_text(
        """file "a.template"
{
  pattern { P }
          { "$(PREF)" }
}
"""
    )

    base_version = wrapper.get_env_var("EPICS_VERSION_NUMBER")
    common_dir = wrapper.module_dir / f"O.{base_version}_Common"

    rc, *_ = wrapper.run_make("db_internal")
    assert rc == 0

    expanded_template_file = common_dir / "a.db"
    assert expanded_template_file.read_text() == template_file_contents

    expanded_substitution_file = common_dir / "b.db"
    assert expanded_substitution_file.read_text() == template_file_contents.replace(
        "$(P)", "$(PREF)"
    )


def test_recursive_db_dependency(wrappers):
    wrapper_main: Wrapper = wrappers.get()
    wrapper_dep: Wrapper = wrappers.get()

    cell_path = wrapper_main.path / "cellMods"
    common_dir = wrapper_main.module_dir / f"O.{wrapper_main.base_version}_Common"

    wrapper_main.add_var_to_config_module(
        f"{wrapper_dep.name}_DEP_VERSION", wrapper_dep.version
    )
    substitution_file = wrapper_main.module_dir / "a.substitutions"
    substitution_file.write_text(
        """file "b.template"
{
    pattern { PATTERN }
            { SUBST }
}"""
    )
    wrapper_main.add_var_to_module_makefile("SUBS", "a.substitutions")
    wrapper_main.add_var_to_module_makefile(
        "USR_DBFLAGS",
        f"-I $(EPICS_MODULES)/{wrapper_dep.name}/$({wrapper_dep.name}_VERSION)/db",
    )

    template_file = wrapper_dep.module_dir / "b.template"
    template_file.write_text("record(ai, $(PATTERN)) {}")
    wrapper_dep.add_var_to_module_makefile("TEMPLATES", "b.template")

    rc, *_ = wrapper_dep.run_make("install", cell_path=cell_path)
    assert rc == 0

    rc, *_ = wrapper_main.run_make("cellbuild", cell_path=cell_path)
    assert rc == 0

    assert "SUBST" in (common_dir / "a.db").read_text()


def test_substitution_template_priority(wrapper: Wrapper):
    """Test that the automated template/substitution file expansion works."""

    wrapper.add_var_to_module_makefile("TMPS", "a.template")
    wrapper.add_var_to_module_makefile("SUBS", "a.substitutions")

    template_file = wrapper.module_dir / "a.template"
    template_file_contents = "record (ai, $(FOO)) {}"
    template_file.write_text(template_file_contents)

    substitution_file = wrapper.module_dir / "a.substitutions"
    substitution_file.write_text(
        """file "a.template"
{
  pattern { FOO }
          { "$(BAR)" }
}
"""
    )

    base_version = wrapper.get_env_var("EPICS_VERSION_NUMBER")
    common_dir = wrapper.module_dir / f"O.{base_version}_Common"

    rc, *_ = wrapper.run_make("db_internal")
    assert rc == 0

    expanded_template_file = common_dir / "a.db"
    assert "BAR" in expanded_template_file.read_text()
    assert "FOO" not in expanded_template_file.read_text()


@pytest.mark.parametrize(
    "installed_archs, param, expected",
    [
        ("foo bar baz foo-bar", "ARCH_FILTER=foo", ["foo"]),
        ("foo", "EXCLUDE_ARCHS=foo", []),
        ("foo-bar foo-baz baz baz-qux", "EXCLUDE_ARCHS=foo", ["baz", "baz-qux"]),
    ],
)
def test_arch_filter(wrapper: Wrapper, installed_archs, param, expected):
Simon Rose's avatar
Simon Rose committed
    arch_regex = re.compile(r"Pass 2: T_A =\s*([^\s]+)")
    wrapper.add_var_to_module_makefile(
        "CROSS_COMPILER_TARGET_ARCHS", installed_archs, modifier=""
    )

    rc, o, _ = wrapper.run_make("debug", param)

    assert rc == 0

    host_arch = os.getenv("EPICS_HOST_ARCH")
    build_archs = [arch for arch in arch_regex.findall(o) if arch != host_arch]

    assert build_archs == expected


@pytest.mark.parametrize("archs,failing_arch", [("foo bar", "foo"), ("foo bar", "bar")])
def test_build_fails_if_nth_architecture_fails(wrapper: Wrapper, archs, failing_arch):
    # LIBOBJS is determined in part based on configuration data coming from
    # $(CONFIG)/os/CONFIG.Common.$(T_A); since our architectures do not actually
    # exist, we need to manually define these /before/ driver.makefile is included.
    makefile_content = wrapper.module_makefile.read_text()
    with open(wrapper.module_makefile, "w") as f:
        f.write(
            f"""ifeq ($(T_A),{failing_arch})
                LIBOBJS = nonexistent_{failing_arch}.o
                endif
                """
        )

    wrapper.add_var_to_config_module("OS_CLASS", "Linux")

    # Skip the host architecture, we are not testing it.
    host_arch = os.getenv("EPICS_HOST_ARCH")
    wrapper.add_var_to_module_makefile("EXCLUDE_ARCHS", host_arch)
    wrapper.add_var_to_module_makefile(
        "CROSS_COMPILER_TARGET_ARCHS", archs, modifier=""
    )
    wrapper.add_var_to_module_makefile("SOURCES", "-none-")

    rc, _, errs = wrapper.run_make("build")
    assert rc == 2
    assert re.search(
        RE_MISSING_FILE.format(filename=re.escape(f"nonexistent_{failing_arch}.o")),
        errs,
    )
@pytest.mark.parametrize("module_version", ["0.0.0+0", "test"])
def test_double_install_fails(wrapper: Wrapper, module_version):
    RE_ERR_MOD_VER_EXISTS = ".*{module}/{version}.* already exists"

    rc, *_ = wrapper.run_make("install", module_version=module_version)
    rc, _, errs = wrapper.run_make("install", module_version=module_version)
    assert rc == 2
    assert re.search(
        RE_ERR_MOD_VER_EXISTS.format(
            module=re.escape(wrapper.name), version=re.escape(module_version)
        ),


@pytest.mark.parametrize(
    "targets",
    [
        ["cellbuild", "build"],
        ["cellinstall", "install"],
        ["vars", "cellbuild"],
        ["debug", "cellinstall"],
        ["cellbuild", "uninstall"],
    ],
    ids=["build", "install", "vars", "debug", "uninstall"],
)
def test_target_fails_with_celltarget(wrapper: Wrapper, targets):
    rc, _, errs = wrapper.run_make(targets[0], targets[1])
    assert rc != 0
    assert (
        "cell targets cannot be called with non cell targets on the same command line"
        in errs
    )


def test_metadata_installed_on_success(wrapper: Wrapper):
    rc, *_ = wrapper.run_make("install")
    assert rc == 0
    assert (wrapper.package_dir / f"{wrapper.name}_meta.yaml").exists()


def test_metadata_not_installed_if_arch_fails(wrapper: Wrapper):
    cross_arch = "foo"
    wrapper.add_var_to_module_makefile(f"VLIBS_{cross_arch}", "not_a_library")
    rc, *_ = wrapper.run_make(
        "install", CROSS_COMPILER_TARGET_ARCHS=cross_arch, OS_CLASS="Linux"
    )
    assert rc == 2
    assert not (wrapper.package_dir / f"{wrapper.name}_meta.yaml").exists()


def test_missing_record_dbd_file_causes_build_failure(wrapper: Wrapper):
    wrapper.add_file("fooRecord.c")
    wrapper.add_var_to_module_makefile("SOURCES", "fooRecord.c")

    rc, _, errs = wrapper.run_make("build")
    assert rc != 0
    assert re.search(
        RE_MISSING_FILE.format(
            filename=re.escape(f"../O.{wrapper.base_version}_Common/fooRecord.dbd")
        ),
        errs,
    )


def test_install_license(wrapper: Wrapper):
    wrapper.add_file("LICENSE")
    wrapper.add_directory("foo")
    wrapper.add_file("foo/LICENSE")

    rc, out, _ = wrapper.run_make("install")
    assert rc == 0
    assert re.search("Installing license file LICENSE", out)
    assert re.search("Installing license file foo/LICENSE", out)

    file_path = wrapper.package_dir / "doc/LICENSE"
    assert file_path.exists()

    file_path = wrapper.package_dir / "doc/foo/LICENSE"
    assert file_path.exists()