From fa8a73cfd282497795cfd34c155065f6ef34de48 Mon Sep 17 00:00:00 2001 From: Guy Sheffer Date: Tue, 17 Feb 2026 20:15:06 +0200 Subject: [PATCH] Extract setup_qemu_static() and add test coverage for QEMU binary copy logic The QEMU binary copy logic was duplicated between execute_chroot_script() in src/custompios and chroot_correct_qemu() in src/common.sh. The inline version in custompios also had a bug: the condition used || instead of &&, making it always true (a tautology). This commit: - Extracts the copy logic into a new setup_qemu_static() function in common.sh, fixing the || to && bug and adding arch-match escape hatches - Fixes the aarch64->armv7l case to copy qemu-arm-static (not qemu-aarch64-static) - Replaces both inline copy blocks with calls to setup_qemu_static() - Adds 24 new test cases covering all host/target/OS combinations for the QEMU binary copy logic Related to PR #261 and issue #262. Co-authored-by: Cursor --- src/common.sh | 55 +++++++++++++++------- src/custompios | 23 +--------- tests/test_qemu_setup.sh | 98 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 134 insertions(+), 42 deletions(-) diff --git a/src/common.sh b/src/common.sh index a54b61a0..3dc9b970 100755 --- a/src/common.sh +++ b/src/common.sh @@ -596,43 +596,64 @@ function load_module_config() { done } -function chroot_correct_qemu() { +function setup_qemu_static() { + # Copy the correct qemu-*-static binary into the chroot, if needed. + # Usage: setup_qemu_static host_arch target_arch local host_arch="$1" local target_arch="$2" - local chroot_script="$3" - local custom_pi_os_path="$4" - # Validate inputs if [[ -z "$host_arch" ]] || [[ -z "$target_arch" ]]; then - echo "Error: Missing required arguments" - echo "Usage: setup_qemu_chroot host_arch target_arch chroot_script custom_pi_os_path" + echo "Error: setup_qemu_static requires host_arch and target_arch" return 1 - fi - - # Copy required scripts - cp "$chroot_script" chroot_script - chmod 755 chroot_script - cp "${custom_pi_os_path}/common.sh" common.sh - chmod 755 common.sh + fi - # Set up QEMU if needed + # Host is not ARM at all (e.g. x86_64) -- need full QEMU emulation if [[ "$host_arch" != "armv7l" ]] && [[ "$host_arch" != "aarch64" ]]; then if [[ "$target_arch" == "armv7l" ]] || [[ "$target_arch" == "armhf" ]]; then if grep -q gentoo /etc/os-release; then ROOT="$(realpath .)" emerge --usepkgonly --oneshot --nodeps qemu else - cp "$(which qemu-arm-static)" usr/bin/qemu-arm-static + if [[ "$target_arch" != "$host_arch" ]]; then + cp "$(which qemu-arm-static)" usr/bin/qemu-arm-static + fi fi elif [[ "$target_arch" == "aarch64" ]] || [[ "$target_arch" == "arm64" ]]; then if grep -q gentoo /etc/os-release; then ROOT="$(realpath .)" emerge --usepkgonly --oneshot --nodeps qemu else - cp "$(which qemu-aarch64-static)" usr/bin/qemu-aarch64-static + if [[ "$target_arch" != "$host_arch" ]]; then + cp "$(which qemu-aarch64-static)" usr/bin/qemu-aarch64-static + fi fi fi elif [[ ( "$target_arch" == "armv7l" || "$target_arch" == "armhf" ) && "$host_arch" != "armv7l" ]]; then - cp "$(which qemu-aarch64-static)" usr/bin/qemu-aarch64-static + # aarch64 host building armv7l/armhf: need 32-bit ARM emulation + cp "$(which qemu-arm-static)" usr/bin/qemu-arm-static fi + # Otherwise: same arch or armv7l host -- no QEMU needed +} + +function chroot_correct_qemu() { + local host_arch="$1" + local target_arch="$2" + local chroot_script="$3" + local custom_pi_os_path="$4" + + # Validate inputs + if [[ -z "$host_arch" ]] || [[ -z "$target_arch" ]]; then + echo "Error: Missing required arguments" + echo "Usage: setup_qemu_chroot host_arch target_arch chroot_script custom_pi_os_path" + return 1 + fi + + # Copy required scripts + cp "$chroot_script" chroot_script + chmod 755 chroot_script + cp "${custom_pi_os_path}/common.sh" common.sh + chmod 755 common.sh + + # Set up QEMU if needed + setup_qemu_static "$host_arch" "$target_arch" # Execute chroot with appropriate QEMU setup if [[ "$host_arch" != "armv7l" ]] && [[ "$host_arch" != "aarch64" ]] && [[ "$host_arch" != "arm64" ]]; then diff --git a/src/custompios b/src/custompios index 16f1affd..884b6486 100755 --- a/src/custompios +++ b/src/custompios @@ -30,27 +30,7 @@ function execute_chroot_script() { fi #black magic of qemu-arm-static - if [ "$(uname -m)" != "armv7l" ] && [ "$(uname -m)" != "aarch64" ] ; then - if [ "$BASE_ARCH" == "armv7l" ] || [ "$BASE_ARCH" == "armhf" ]; then - if (grep -q gentoo /etc/os-release);then - ROOT="`realpath .`" emerge --usepkgonly --oneshot --nodeps qemu - else - if [ "${BASE_ARCH}" != "$(uname -m)" ]; then - cp `which qemu-arm-static` usr/bin/qemu-arm-static - fi - fi - elif [ "$BASE_ARCH" == "aarch64" ] || [ "$BASE_ARCH" == "arm64" ]; then - if (grep -q gentoo /etc/os-release);then - ROOT="`realpath .`" emerge --usepkgonly --oneshot --nodeps qemu - else - if [ "${BASE_ARCH}" != "$(uname -m)" ]; then - cp `which qemu-aarch64-static` usr/bin/qemu-aarch64-static - fi - fi - fi - elif [[ ( "$BASE_ARCH" == "armv7l" || "$BASE_ARCH" == "armhf" ) && "$(uname -m)" != "armv7l" ]]; then - cp `which qemu-aarch64-static` usr/bin/qemu-aarch64-static - fi + setup_qemu_static "$(uname -m)" "$BASE_ARCH" cp $2 chroot_script chmod 755 chroot_script @@ -184,6 +164,7 @@ pushd "${BASE_WORKSPACE}" fi echo $ARMBIAN_CONFIG_TXT_FILE # if you need anything from common running in execute_chroot_script, export it here + export -f setup_qemu_static export -f chroot_correct_qemu export -f execute_chroot_script bash -x "${CHROOT_SCRIPT}" diff --git a/tests/test_qemu_setup.sh b/tests/test_qemu_setup.sh index 55cb5a6b..7d5ec47c 100755 --- a/tests/test_qemu_setup.sh +++ b/tests/test_qemu_setup.sh @@ -39,7 +39,43 @@ setup_mocks() { export -f realpath } -# Define test matrix +# Define test matrix for setup_qemu_static (QEMU binary copy logic) +# Expected patterns match against the mocked cp/emerge output +declare -A copy_test_matrix=( + # x86_64 host -- always needs qemu + ["x86_64:armv7l:ubuntu"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" + ["x86_64:armhf:ubuntu"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" + ["x86_64:aarch64:ubuntu"]="Copying /usr/bin/qemu-aarch64-static to usr/bin/qemu-aarch64-static" + ["x86_64:arm64:ubuntu"]="Copying /usr/bin/qemu-aarch64-static to usr/bin/qemu-aarch64-static" + ["x86_64:armv7l:gentoo"]="Emerging qemu" + ["x86_64:armhf:gentoo"]="Emerging qemu" + ["x86_64:aarch64:gentoo"]="Emerging qemu" + ["x86_64:arm64:gentoo"]="Emerging qemu" + + # armv7l host -- never needs qemu (native for armv7l/armhf, no support for aarch64) + ["armv7l:armv7l:ubuntu"]="" + ["armv7l:armhf:ubuntu"]="" + ["armv7l:aarch64:ubuntu"]="" + ["armv7l:arm64:ubuntu"]="" + ["armv7l:armv7l:gentoo"]="" + ["armv7l:armhf:gentoo"]="" + ["armv7l:aarch64:gentoo"]="" + ["armv7l:arm64:gentoo"]="" + + # aarch64 host building aarch64/arm64 -- no qemu needed (native) + ["aarch64:aarch64:ubuntu"]="" + ["aarch64:arm64:ubuntu"]="" + ["aarch64:aarch64:gentoo"]="" + ["aarch64:arm64:gentoo"]="" + + # aarch64 host building armv7l/armhf -- needs qemu-arm-static + ["aarch64:armv7l:ubuntu"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" + ["aarch64:armhf:ubuntu"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" + ["aarch64:armv7l:gentoo"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" + ["aarch64:armhf:gentoo"]="Copying /usr/bin/qemu-arm-static to usr/bin/qemu-arm-static" +) + +# Define test matrix for chroot_correct_qemu (chroot execution logic) declare -A test_matrix=( # x86_64 host ["x86_64:armv7l:gentoo"]="gentoo" @@ -113,7 +149,48 @@ print_test_matrix() { echo } -# Run a single test case +# Run a single copy test case (for setup_qemu_static) +run_copy_test_case() { + local host="$1" + local target="$2" + local os="$3" + local test_name="QEMU Copy Test" + local key="${host}:${target}:${os}" + local expected_pattern="${copy_test_matrix[$key]}" + + print_test_header "$test_name" + echo "Parameters:" + echo " Host Architecture: $host" + echo " Target Architecture: $target" + echo " Operating System: $os" + echo " Expected Pattern: ${expected_pattern:-(empty output)}" + + MOCK_OS="$os" + local output=$(setup_qemu_static "$host" "$target" 2>&1) + local is_passed=false + + if [[ -z "$expected_pattern" ]]; then + # Expect empty output (no qemu copy needed) + if [[ -z "$output" ]]; then + is_passed=true + fi + elif [[ "$output" =~ $expected_pattern ]]; then + is_passed=true + fi + + echo -e "${BLUE}Command Output:${NC}" + echo "${output:-(empty)}" + + print_test_result "$test_name" "$is_passed" "$output" "${expected_pattern:-(empty output)}" + + if $is_passed; then + return 0 + else + return 1 + fi +} + +# Run a single test case (for chroot_correct_qemu) run_test_case() { local host="$1" local target="$2" @@ -204,8 +281,21 @@ run_tests() { # Print test matrix print_test_matrix - # Run architecture combination tests - echo -e "${BLUE}Running Architecture Combination Tests${NC}" + # Run QEMU copy tests (setup_qemu_static) + echo -e "${BLUE}Running QEMU Binary Copy Tests (setup_qemu_static)${NC}" + for key in "${!copy_test_matrix[@]}"; do + IFS=':' read -r host target os <<< "$key" + ((test_count++)) + + if run_copy_test_case "$host" "$target" "$os"; then + ((tests_passed++)) + else + failed_tests+=("copy:$key") + fi + done + + # Run architecture combination tests (chroot_correct_qemu) + echo -e "${BLUE}Running Chroot Execution Tests (chroot_correct_qemu)${NC}" for key in "${!test_matrix[@]}"; do IFS=':' read -r host target os <<< "$key" ((test_count++))