Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
781 changes: 781 additions & 0 deletions bin/lib/base_library_builder.py

Large diffs are not rendered by default.

562 changes: 86 additions & 476 deletions bin/lib/fortran_library_builder.py

Large diffs are not rendered by default.

700 changes: 99 additions & 601 deletions bin/lib/library_builder.py

Large diffs are not rendered by default.

265 changes: 70 additions & 195 deletions bin/lib/rust_library_builder.py

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions bin/test/base_library_builder_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
from __future__ import annotations

from unittest.mock import MagicMock

import pytest
from lib.base_library_builder import CONANINFOHASH_RE, BaseLibraryBuilder
from lib.library_build_config import LibraryBuildConfig


@pytest.fixture
def test_builder():
"""Create a test builder instance."""
logger = MagicMock()
install_context = MagicMock()
install_context.dry_run = False
buildconfig = LibraryBuildConfig({
"lib_type": "static",
"package_dir": "libs",
"source_archive": "",
})

# Create a concrete test class since BaseLibraryBuilder is abstract
class TestBuilder(BaseLibraryBuilder):
def completeBuildConfig(self):
pass

def makebuild(self, buildfor):
pass

def makebuildfor(self, *args):
pass

def writeconanfile(self, buildfolder):
pass

return TestBuilder(
logger=logger,
language="c++",
libname="testlib",
target_name="1.0",
sourcefolder="/tmp/test",
install_context=install_context,
buildconfig=buildconfig,
)


def test_setCurrentConanBuildParameters_format(test_builder):
"""Test that conan parameters are built in the correct format."""
# Call the method with typical parameters
test_builder.setCurrentConanBuildParameters(
buildos="Linux",
buildtype="Debug",
compilerTypeOrGcc="gcc",
compiler="11.1.0",
libcxx="libstdc++",
arch="x86_64",
stdver="c++17",
extraflags="",
)

# Expected format: alternating "-s" and "key=value" as separate list items
expected = [
"-s",
"os=Linux",
"-s",
"build_type=Debug",
"-s",
"compiler=gcc",
"-s",
"compiler.version=11.1.0",
"-s",
"compiler.libcxx=libstdc++",
"-s",
"arch=x86_64",
"-s",
"stdver=c++17",
"-s",
"flagcollection=",
]

assert test_builder.current_buildparameters == expected


def test_setCurrentConanBuildParameters_with_defaults(test_builder):
"""Test parameter building with None/empty values that trigger defaults."""
test_builder.setCurrentConanBuildParameters(
buildos="Linux",
buildtype="Release",
compilerTypeOrGcc=None, # Should default to "gcc"
compiler="10.2.0",
libcxx=None, # Should default to "libstdc++"
arch="x86",
stdver="",
extraflags="",
)

expected = [
"-s",
"os=Linux",
"-s",
"build_type=Release",
"-s",
"compiler=gcc", # defaulted
"-s",
"compiler.version=10.2.0",
"-s",
"compiler.libcxx=libstdc++", # defaulted
"-s",
"arch=x86",
"-s",
"stdver=",
"-s",
"flagcollection=",
]

assert test_builder.current_buildparameters == expected


def test_setCurrentConanBuildParameters_includes_flagcollection(test_builder):
"""Test that flagcollection is included in conan parameters list."""
test_builder.setCurrentConanBuildParameters(
buildos="Linux",
buildtype="Debug",
compilerTypeOrGcc="clang",
compiler="14.0.0",
libcxx="libc++",
arch="x86_64",
stdver="c++20",
extraflags="-O3",
)

# Verify flagcollection is in the object
assert test_builder.current_buildparameters_obj["flagcollection"] == "-O3"

# Verify it IS in the parameter list (original behavior)
param_strings = " ".join(test_builder.current_buildparameters)
assert "flagcollection=-O3" in param_strings


def test_conan_parameter_format_for_command_line(test_builder):
"""Test that parameters can be used directly with conan command."""
test_builder.setCurrentConanBuildParameters("Linux", "Debug", "gcc", "11.1.0", "libstdc++", "x86_64", "c++17", "")

# Simulate building a conan command
conan_cmd = ["conan", "info", "."] + test_builder.current_buildparameters

# Verify the command would be correctly formatted
expected_cmd = [
"conan",
"info",
".",
"-s",
"os=Linux",
"-s",
"build_type=Debug",
"-s",
"compiler=gcc",
"-s",
"compiler.version=11.1.0",
"-s",
"compiler.libcxx=libstdc++",
"-s",
"arch=x86_64",
"-s",
"stdver=c++17",
"-s",
"flagcollection=",
]

assert conan_cmd == expected_cmd


def test_conan_hash_regex_with_real_output():
"""Test that the regex pattern matches actual conan info output."""
# Test real conan info output format (based on what's expected from conan)
conan_output = """
[project/1.0@celibs/trunk]
ID: 5ab84d28a1f62d3983d85f5b69d0e4e45741e7e9
BuildID: None
Context: host
"""

match = CONANINFOHASH_RE.search(conan_output)
assert match is not None
assert match.group(1) == "5ab84d28a1f62d3983d85f5b69d0e4e45741e7e9"


@pytest.mark.parametrize(
"text,expected_hash",
[
# Standard conan output formats we expect
(" ID: abc123def456", "abc123def456"),
("\tID: 789abc", "789abc"),
(" ID: 5ab84d28a1f62d3983d85f5b69d0e4e45741e7e9", "5ab84d28a1f62d3983d85f5b69d0e4e45741e7e9"),
# Edge case: empty hash (allowed by \w*)
(" ID: ", ""),
],
)
def test_conan_hash_regex_valid_patterns(text, expected_hash):
"""Test regex matches valid ID patterns from conan output."""
match = CONANINFOHASH_RE.search(text)
assert match is not None
assert match.group(1) == expected_hash


@pytest.mark.parametrize(
"text",
[
"ID: abc123", # No whitespace before ID:
" ID:abc123", # No whitespace after ID:
],
)
def test_conan_hash_regex_invalid_patterns(text):
"""Test regex correctly rejects invalid patterns."""
match = CONANINFOHASH_RE.search(text)
assert match is None
12 changes: 8 additions & 4 deletions bin/test/fortran_library_builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ def test_get_conan_hash_success(mock_subprocess, requests_mock):
def test_execute_build_script_success(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_fortran_test_build_config()
builder = FortranLibraryBuilder(
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
Expand All @@ -200,14 +201,17 @@ def test_execute_build_script_success(mock_subprocess, requests_mock):
result = builder.executebuildscript("/tmp/buildfolder")

assert result == BuildStatus.Ok
mock_subprocess.assert_called_once_with(["./cebuild.sh"], cwd="/tmp/buildfolder", timeout=600)
mock_subprocess.assert_called_once_with(
["bash", "/tmp/buildfolder/cebuild.sh"], cwd="/tmp/buildfolder", timeout=600
)


@patch("subprocess.call")
def test_execute_build_script_timeout(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_fortran_test_build_config()
builder = FortranLibraryBuilder(
logger, "fortran", "fortranlib", "2.0.0", "/tmp/source", install_context, build_config, False
Expand All @@ -220,7 +224,7 @@ def test_execute_build_script_timeout(mock_subprocess, requests_mock):
assert result == BuildStatus.TimedOut


@patch("lib.fortran_library_builder.get_ssm_param")
@patch("lib.base_library_builder.get_ssm_param")
def test_conanproxy_login_success(mock_get_ssm, requests_mock):
requests_mock.get(f"{BASE}fortran.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
Expand Down
12 changes: 7 additions & 5 deletions bin/test/library_builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def test_get_toolchain_path_from_options_none(requests_mock):

options = "-O2 -std=c++17"
result = builder.getToolchainPathFromOptions(options)
assert result is False
assert not result


def test_get_sysroot_path_from_options(requests_mock):
Expand Down Expand Up @@ -251,7 +251,8 @@ def test_get_conan_hash_success(mock_subprocess, requests_mock):
def test_execute_build_script_success(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}cpp.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_test_build_config()
builder = LibraryBuilder(
logger, "cpp", "testlib", "1.0.0", "/tmp/source", install_context, build_config, False, LibraryPlatform.Linux
Expand All @@ -269,7 +270,8 @@ def test_execute_build_script_success(mock_subprocess, requests_mock):
def test_execute_build_script_timeout(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}cpp.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_test_build_config()
builder = LibraryBuilder(
logger, "cpp", "testlib", "1.0.0", "/tmp/source", install_context, build_config, False, LibraryPlatform.Linux
Expand All @@ -282,7 +284,7 @@ def test_execute_build_script_timeout(mock_subprocess, requests_mock):
assert result == BuildStatus.TimedOut


@patch("lib.library_builder.get_ssm_param")
@patch("lib.base_library_builder.get_ssm_param")
def test_conanproxy_login_success(mock_get_ssm, requests_mock):
requests_mock.get(f"{BASE}cpp.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
Expand All @@ -305,7 +307,7 @@ def test_conanproxy_login_success(mock_get_ssm, requests_mock):
mock_get_ssm.assert_called_once_with("/compiler-explorer/conanpwd")


@patch("lib.library_builder.get_ssm_param")
@patch("lib.base_library_builder.get_ssm_param")
def test_conanproxy_login_with_env_var(mock_get_ssm, requests_mock):
requests_mock.get(f"{BASE}cpp.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
Expand Down
12 changes: 7 additions & 5 deletions bin/test/rust_library_builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_execute_build_script_timeout(mock_subprocess, requests_mock):
assert result == BuildStatus.TimedOut


@patch("lib.rust_library_builder.get_ssm_param")
@patch("lib.base_library_builder.get_ssm_param")
def test_conanproxy_login_success(mock_get_ssm, requests_mock):
requests_mock.get(f"{BASE}rust.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
Expand Down Expand Up @@ -141,13 +141,14 @@ def test_get_commit_hash(requests_mock):
def test_execute_conan_script_success(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}rust.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_rust_test_build_config()
builder = RustLibraryBuilder(logger, "rust", "rustlib", "1.0.0", install_context, build_config)

mock_subprocess.return_value = 0

result = builder.executeconanscript("/tmp/buildfolder", "x86_64", "")
result = builder.validate_and_export_conan("/tmp/buildfolder", "x86_64", "")

assert result == BuildStatus.Ok
mock_subprocess.assert_called_once_with(["./conanexport.sh"], cwd="/tmp/buildfolder")
Expand All @@ -157,13 +158,14 @@ def test_execute_conan_script_success(mock_subprocess, requests_mock):
def test_execute_conan_script_failure(mock_subprocess, requests_mock):
requests_mock.get(f"{BASE}rust.amazon.properties", text="")
logger = mock.Mock(spec_set=Logger)
install_context = mock.Mock(spec_set=InstallationContext)
install_context = mock.Mock()
install_context.dry_run = False
build_config = create_rust_test_build_config()
builder = RustLibraryBuilder(logger, "rust", "rustlib", "1.0.0", install_context, build_config)

mock_subprocess.return_value = 1

result = builder.executeconanscript("/tmp/buildfolder", "x86_64", "")
result = builder.validate_and_export_conan("/tmp/buildfolder", "x86_64", "")

assert result == BuildStatus.Failed

Expand Down
Loading