cmake_minimum_required(VERSION 3.20)

project(BMCoCo-Pi VERSION 0.1.0 LANGUAGES CXX)

add_library(bmcoco_web_http src/web_http.cpp)
target_include_directories(bmcoco_web_http PUBLIC include)
target_compile_features(bmcoco_web_http PUBLIC cxx_std_17)
target_compile_options(bmcoco_web_http PRIVATE -Wall -Wextra -Wpedantic)
find_package(Python3 COMPONENTS Interpreter REQUIRED)
set(web_asset_source "${CMAKE_CURRENT_SOURCE_DIR}/web/file_manager")
set(web_asset_output "${CMAKE_CURRENT_BINARY_DIR}/web_assets_generated.cpp")
add_custom_command(OUTPUT "${web_asset_output}"
    COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/embed_web_assets.py"
        --source "${web_asset_source}" --output "${web_asset_output}"
    DEPENDS tools/embed_web_assets.py web/file_manager/index.html
        web/file_manager/style.css web/file_manager/app.mjs web/file_manager/sha256.mjs web/file_manager/navigation.mjs web/file_manager/attract.mjs
    VERBATIM)
add_library(bmcoco_web_assets "${web_asset_output}")
target_include_directories(bmcoco_web_assets PUBLIC include)
target_compile_features(bmcoco_web_assets PUBLIC cxx_std_17)

option(BMCOCO_BUILD_TESTS "Build BMCoCo-Pi host tests" ON)
option(BMCOCO_BUILD_XROAR_PROOF "Build pinned, read-only CoCo 2 core proof" OFF)
if(BMCOCO_BUILD_XROAR_PROOF)
    enable_language(C)
    include(platform/xroar_proof/core.cmake)
endif()

add_library(bmcoco_control
    src/control_model.cpp
    src/drivewire.cpp
    src/emulator_adapter.cpp
)

target_include_directories(bmcoco_control
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_features(bmcoco_control PUBLIC cxx_std_17)
target_compile_options(bmcoco_control PRIVATE -Wall -Wextra -Wpedantic)

if(BMCOCO_BUILD_TESTS)
    enable_testing()
    add_executable(attract_mode_test tests/attract_mode_test.cpp)
    target_link_libraries(attract_mode_test PRIVATE bmcoco_web_http)
    target_compile_features(attract_mode_test PRIVATE cxx_std_17)
    add_test(NAME attract_mode COMMAND attract_mode_test)
    add_executable(display_blanking_test tests/display_blanking_test.cpp)
    target_include_directories(display_blanking_test PRIVATE include)
    target_compile_features(display_blanking_test PRIVATE cxx_std_17)
    target_compile_options(display_blanking_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME display_blanking COMMAND display_blanking_test)
    add_executable(startup_profile_test tests/startup_profile_test.cpp)
    target_include_directories(startup_profile_test PRIVATE include)
    target_compile_features(startup_profile_test PRIVATE cxx_std_17)
    target_compile_options(startup_profile_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME startup_profile COMMAND startup_profile_test)
    add_executable(usb_controller_test tests/usb_controller_test.cpp)
    target_include_directories(usb_controller_test PRIVATE include)
    target_compile_features(usb_controller_test PRIVATE cxx_std_17)
    target_compile_options(usb_controller_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME usb_controller COMMAND usb_controller_test)
    add_test(NAME sd_presets COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/sd_presets_test.py)
    add_test(NAME install_sd_bundle COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/install_sd_bundle_test.py)
    add_executable(web_http_test tests/web_http_test.cpp)
    target_link_libraries(web_http_test PRIVATE bmcoco_web_http)
    target_compile_options(web_http_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME web_http COMMAND web_http_test)
    add_executable(web_assets_test tests/web_assets_test.cpp)
    target_link_libraries(web_assets_test PRIVATE bmcoco_web_assets)
    add_test(NAME web_assets COMMAND web_assets_test "${web_asset_source}")
    find_program(BMCOCO_NODE_EXECUTABLE node)
    if(BMCOCO_NODE_EXECUTABLE)
        add_test(NAME web_sha256 COMMAND ${BMCOCO_NODE_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/tests/web_sha256_test.mjs)
    endif()
    add_executable(guest_options_test tests/guest_options_test.cpp)
    target_include_directories(guest_options_test PRIVATE include)
    target_compile_features(guest_options_test PRIVATE cxx_std_17)
    add_test(NAME guest_options COMMAND guest_options_test)
    add_executable(remote_snapshot_test tests/remote_snapshot_test.cpp)
    target_include_directories(remote_snapshot_test PRIVATE include)
    target_compile_features(remote_snapshot_test PRIVATE cxx_std_17)
    target_compile_options(remote_snapshot_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME remote_snapshot COMMAND remote_snapshot_test)
    add_executable(web_screen_test tests/web_screen_test.cpp)
    target_link_libraries(web_screen_test PRIVATE bmcoco_web_http)
    target_compile_options(web_screen_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME web_screen COMMAND web_screen_test)

    add_executable(control_model_test
        tests/control_model_test.cpp
    )
    target_link_libraries(control_model_test PRIVATE bmcoco_control)
    target_compile_options(control_model_test PRIVATE -Wall -Wextra -Wpedantic)

    add_test(NAME control_model COMMAND control_model_test)

    add_executable(drivewire_test tests/drivewire_test.cpp)
    target_link_libraries(drivewire_test PRIVATE bmcoco_control)
    target_compile_options(drivewire_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME drivewire COMMAND drivewire_test)

    add_executable(audio_probe_signal_test tests/audio_probe_signal_test.cpp)
    target_link_libraries(audio_probe_signal_test PRIVATE bmcoco_control)
    target_compile_options(audio_probe_signal_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME audio_probe_signal COMMAND audio_probe_signal_test)

    add_executable(audio_probe_driver_test
        tests/audio_probe_driver_test.cpp
        platform/circle/pi500_probe/audio_probe.cpp
    )
    target_include_directories(audio_probe_driver_test PRIVATE
        tests/circle_audio_fake
        platform/circle/pi500_probe
        include
    )
    target_compile_features(audio_probe_driver_test PRIVATE cxx_std_17)
    target_compile_options(audio_probe_driver_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME audio_probe_driver COMMAND audio_probe_driver_test)

    add_executable(frame_pacer_test tests/frame_pacer_test.cpp)
    target_link_libraries(frame_pacer_test PRIVATE bmcoco_control)
    target_compile_options(frame_pacer_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME frame_pacer COMMAND frame_pacer_test)

    add_executable(network_probe_status_test tests/network_probe_status_test.cpp)
    target_link_libraries(network_probe_status_test PRIVATE bmcoco_control)
    target_compile_options(network_probe_status_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME network_probe_status COMMAND network_probe_status_test)

    add_executable(guest_audio_test tests/guest_audio_test.cpp
        platform/circle/pi500_guest/audio.cpp)
    target_include_directories(guest_audio_test PRIVATE tests/circle_audio_fake
        platform/circle/pi500_guest include)
    target_compile_features(guest_audio_test PRIVATE cxx_std_17)
    target_compile_options(guest_audio_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME guest_audio COMMAND guest_audio_test)
    add_executable(guest_realtime_test tests/guest_realtime_test.cpp)
    target_include_directories(guest_realtime_test PRIVATE include)
    target_compile_features(guest_realtime_test PRIVATE cxx_std_17)
    target_compile_options(guest_realtime_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME guest_realtime COMMAND guest_realtime_test)
    add_executable(network_startup_test tests/network_startup_test.cpp)
    target_include_directories(network_startup_test PRIVATE include)
    target_compile_features(network_startup_test PRIVATE cxx_std_17)
    add_test(NAME network_startup COMMAND network_startup_test)
    add_executable(guest_menu_test tests/guest_menu_test.cpp)
    target_include_directories(guest_menu_test PRIVATE include)
    target_compile_features(guest_menu_test PRIVATE cxx_std_17)
    target_compile_options(guest_menu_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME guest_menu COMMAND guest_menu_test)
    add_executable(osd_text_entry_test tests/osd_text_entry_test.cpp)
    target_include_directories(osd_text_entry_test PRIVATE include)
    target_compile_features(osd_text_entry_test PRIVATE cxx_std_17)
    target_compile_options(osd_text_entry_test PRIVATE -Wall -Wextra -Wpedantic)
    add_test(NAME osd_text_entry COMMAND osd_text_entry_test)
    if(BMCOCO_BUILD_XROAR_PROOF AND NOT CMAKE_CROSSCOMPILING)
        set(BMCOCO_CIRCLE_ROOT "/home/ron/source/BMCoCo-deps/circle-stdlib/libs/circle"
            CACHE PATH "Pinned read-only Circle source for filesystem test")
        execute_process(COMMAND git -C "${BMCOCO_CIRCLE_ROOT}" rev-parse HEAD
            OUTPUT_VARIABLE storage_revision OUTPUT_STRIP_TRAILING_WHITESPACE)
        if(NOT storage_revision STREQUAL "7a81e1b480fe7d19fc1fc695b89bff0a0848f44b")
            message(FATAL_ERROR "Storage test requires the documented Circle revision")
        endif()
        find_program(MKFS_FAT NAMES mkfs.fat PATHS /usr/sbin REQUIRED)
        include(platform/remote/crypto.cmake)
        add_executable(guest_storage_test tests/guest_storage_test.cpp tests/web_files_test.cpp
            platform/circle/pi500_guest/storage.cpp
            platform/circle/pi500_guest/files.cpp
            platform/circle/pi500_guest/web_files.cpp
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ff.c"
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ffunicode.c")
        target_include_directories(guest_storage_test PRIVATE
            platform/circle/pi500_guest)
        # addon/linux must not shadow native Debian's linux/* headers.
        target_compile_options(guest_storage_test PRIVATE
            -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_features(guest_storage_test PRIVATE cxx_std_17)
        target_compile_definitions(guest_storage_test PRIVATE BMCOCO_ENABLE_MOOH_SD_WRITE=1)
        target_link_libraries(guest_storage_test PRIVATE bmcoco_remote bmcoco_web_http)
        # Exercise Pi400's actual promotion filenames against pinned FatFs.
        add_executable(guest_storage_rpi4_test tests/guest_storage_test.cpp tests/web_files_test.cpp
            platform/circle/pi500_guest/storage.cpp platform/circle/pi500_guest/files.cpp
            platform/circle/pi500_guest/web_files.cpp
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ff.c"
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ffunicode.c")
        target_include_directories(guest_storage_rpi4_test PRIVATE platform/circle/pi500_guest)
        target_compile_options(guest_storage_rpi4_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_definitions(guest_storage_rpi4_test PRIVATE RASPPI=4 BMCOCO_ENABLE_MOOH_SD_WRITE=1)
        target_compile_features(guest_storage_rpi4_test PRIVATE cxx_std_17)
        target_link_libraries(guest_storage_rpi4_test PRIVATE bmcoco_remote bmcoco_web_http)
        add_executable(web_native_preview tests/guest_storage_test.cpp tests/web_files_test.cpp
            tests/web_native_preview.cpp platform/circle/pi500_guest/web_server.cpp
            platform/circle/pi500_guest/web_files.cpp platform/circle/pi500_guest/storage.cpp
            platform/circle/pi500_guest/files.cpp "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ff.c"
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ffunicode.c")
        target_include_directories(web_native_preview PRIVATE tests/web_native_net platform/circle/pi500_guest)
        target_compile_options(web_native_preview PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_definitions(web_native_preview PRIVATE BMCOCO_NATIVE_WEB_PREVIEW=1)
        target_compile_features(web_native_preview PRIVATE cxx_std_17)
        target_link_libraries(web_native_preview PRIVATE bmcoco_remote bmcoco_web_http bmcoco_web_assets)
        add_executable(guest_kernel_test tests/guest_kernel_test.cpp
            platform/circle/pi500_guest/kernel.cpp platform/circle/pi500_guest/build_info.cpp platform/circle/pi500_guest/audio.cpp
            platform/circle/pi500_guest/network.cpp platform/circle/pi500_guest/web_files.cpp
            platform/circle/pi500_guest/web_server.cpp src/drivewire.cpp)
        target_link_libraries(guest_kernel_test PRIVATE bmcoco_remote bmcoco_web_http bmcoco_web_assets)
        target_include_directories(guest_kernel_test PRIVATE tests/circle_network_fake tests/circle_guest_fake
            tests/circle_audio_fake platform/circle/pi500_guest platform/xroar_proof/include include)
        target_compile_options(guest_kernel_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_features(guest_kernel_test PRIVATE cxx_std_17)
        add_test(NAME guest_kernel COMMAND guest_kernel_test)
        get_target_property(guest_kernel_sources guest_kernel_test SOURCES)
        get_target_property(guest_kernel_includes guest_kernel_test INCLUDE_DIRECTORIES)
        add_executable(guest_kernel_rpi4_test ${guest_kernel_sources})
        target_link_libraries(guest_kernel_rpi4_test PRIVATE bmcoco_remote bmcoco_web_http bmcoco_web_assets)
        target_include_directories(guest_kernel_rpi4_test PRIVATE ${guest_kernel_includes})
        target_compile_options(guest_kernel_rpi4_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_features(guest_kernel_rpi4_test PRIVATE cxx_std_17)
        target_compile_definitions(guest_kernel_rpi4_test PRIVATE RASPPI=4)
        add_test(NAME guest_kernel_rpi4 COMMAND guest_kernel_rpi4_test)
        add_test(NAME guest_attract COMMAND guest_kernel_test --attract)
        add_test(NAME guest_attract_rpi4 COMMAND guest_kernel_rpi4_test --attract)
        add_test(NAME guest_display COMMAND guest_kernel_test --display)
        add_test(NAME guest_display_rpi4 COMMAND guest_kernel_rpi4_test --display)
        add_test(NAME guest_startup_profile COMMAND guest_kernel_test --startup)
        add_test(NAME guest_startup_profile_rpi4 COMMAND guest_kernel_rpi4_test --startup)
        add_test(NAME guest_usb_controllers COMMAND guest_kernel_test --controllers)
        add_test(NAME guest_usb_controllers_rpi4 COMMAND guest_kernel_rpi4_test --controllers)
        foreach(autonet_family 4 5)
            add_executable(guest_autonet_rpi${autonet_family}_test ${guest_kernel_sources})
            target_link_libraries(guest_autonet_rpi${autonet_family}_test PRIVATE bmcoco_remote bmcoco_web_http bmcoco_web_assets)
            target_include_directories(guest_autonet_rpi${autonet_family}_test PRIVATE ${guest_kernel_includes})
            target_compile_options(guest_autonet_rpi${autonet_family}_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
            target_compile_features(guest_autonet_rpi${autonet_family}_test PRIVATE cxx_std_17)
            target_compile_definitions(guest_autonet_rpi${autonet_family}_test PRIVATE RASPPI=${autonet_family} BMCOCO_AUTO_NETWORK=1)
            add_test(NAME guest_autonet_rpi${autonet_family} COMMAND guest_autonet_rpi${autonet_family}_test --auto-network)
        endforeach()
        add_test(NAME guest_control_recovery COMMAND guest_kernel_test --diagnose-control)
        add_test(NAME guest_control_recovery_rpi4 COMMAND guest_kernel_rpi4_test --diagnose-control)
        add_executable(guest_runtime_test tests/guest_runtime_test.cpp
            platform/circle/pi500_guest/kernel.cpp platform/circle/pi500_guest/build_info.cpp platform/circle/pi500_guest/audio.cpp
            platform/circle/pi500_guest/network.cpp platform/circle/pi500_guest/storage.cpp
            platform/circle/pi500_guest/files.cpp platform/circle/pi500_guest/web_files.cpp
            platform/circle/pi500_guest/web_server.cpp src/drivewire.cpp
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ff.c"
            "${BMCOCO_CIRCLE_ROOT}/addon/fatfs/ffunicode.c")
        target_link_libraries(guest_runtime_test PRIVATE bmcoco_xroar_core bmcoco_remote bmcoco_web_http bmcoco_web_assets)
        target_include_directories(guest_runtime_test PRIVATE ${guest_kernel_includes})
        target_compile_options(guest_runtime_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_features(guest_runtime_test PRIVATE cxx_std_17)
        target_compile_definitions(guest_runtime_test PRIVATE RASPPI=4 BMCOCO_ENABLE_MOOH_SD_WRITE=1 BMCOCO_ENABLE_PHYSICAL_SD_WRITE=1)
        get_target_property(guest_runtime_sources guest_runtime_test SOURCES)
        add_executable(guest_runtime_diagnostic_test ${guest_runtime_sources})
        target_link_libraries(guest_runtime_diagnostic_test PRIVATE bmcoco_xroar_core bmcoco_remote bmcoco_web_http bmcoco_web_assets)
        target_include_directories(guest_runtime_diagnostic_test PRIVATE ${guest_kernel_includes})
        target_compile_options(guest_runtime_diagnostic_test PRIVATE -idirafter "${BMCOCO_CIRCLE_ROOT}/addon")
        target_compile_features(guest_runtime_diagnostic_test PRIVATE cxx_std_17)
        target_compile_definitions(guest_runtime_diagnostic_test PRIVATE RASPPI=4 BMCOCO_BOOT_DIAGNOSTICS=1 BMCOCO_ENABLE_MOOH_SD_WRITE=1 BMCOCO_ENABLE_PHYSICAL_SD_WRITE=1)
        set(BMCOCO_RUNTIME_IMAGE "" CACHE FILEPATH "Optional private image for actual kernel/core/FatFs execution tests")
        set(BMCOCO_MOOH_RUNTIME_IMAGE "" CACHE FILEPATH "Optional private builder image containing MOOH media; tests write only an anonymous copy")
        if(BMCOCO_MOOH_RUNTIME_IMAGE)
            add_test(NAME guest_mooh_runtime COMMAND guest_runtime_test "${BMCOCO_MOOH_RUNTIME_IMAGE}" 640 480 32 --mooh)
            set_tests_properties(guest_mooh_runtime PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
            add_test(NAME guest_web_io_scheduling COMMAND guest_runtime_test "${BMCOCO_MOOH_RUNTIME_IMAGE}" 640 480 32 --web-hash)
            set_tests_properties(guest_web_io_scheduling PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
            add_test(NAME guest_web_io_starvation COMMAND guest_runtime_test "${BMCOCO_MOOH_RUNTIME_IMAGE}" 640 480 32 --web-starvation)
            set_tests_properties(guest_web_io_starvation PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
        endif()
        if(BMCOCO_RUNTIME_IMAGE)
            add_test(NAME guest_attract_runtime COMMAND guest_runtime_test "${BMCOCO_RUNTIME_IMAGE}" 640 480 32 --attract)
            set_tests_properties(guest_attract_runtime PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
            foreach(geometry "640;480;32" "720;480;16" "1280;720;16" "1920;1080;32")
                string(REPLACE ";" "x" display_name "${geometry}")
                add_test(NAME guest_runtime_${display_name} COMMAND guest_runtime_test "${BMCOCO_RUNTIME_IMAGE}" ${geometry})
                set_tests_properties(guest_runtime_${display_name} PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
                add_test(NAME guest_runtime_diag_${display_name} COMMAND guest_runtime_diagnostic_test "${BMCOCO_RUNTIME_IMAGE}" ${geometry})
                set_tests_properties(guest_runtime_diag_${display_name} PROPERTIES TIMEOUT 30 LABELS "local-rom;native-only")
            endforeach()
        endif()
        add_test(NAME guest_boot_profile COMMAND guest_kernel_test --boot-profile)
        add_test(NAME guest_web_kernel COMMAND guest_kernel_test --web)
        add_test(NAME guest_web_kernel_rpi4 COMMAND guest_kernel_rpi4_test --web)
        add_test(NAME guest_cassette_kernel COMMAND guest_kernel_test --cassette)
        add_executable(guest_network_test tests/guest_network_test.cpp
            platform/circle/pi500_guest/network.cpp src/drivewire.cpp)
        target_link_libraries(guest_network_test PRIVATE bmcoco_remote)
        target_include_directories(guest_network_test PRIVATE tests/circle_network_fake
            tests/circle_guest_fake tests/circle_audio_fake platform/circle/pi500_guest)
        target_compile_options(guest_network_test PRIVATE -Wall -Wextra -Wpedantic)
        foreach(scenario ready init bind)
            add_test(NAME guest_network_${scenario} COMMAND guest_network_test ${scenario})
        endforeach()
        add_executable(web_server_test tests/web_server_test.cpp
            platform/circle/pi500_guest/web_server.cpp)
        target_link_libraries(web_server_test PRIVATE bmcoco_web_http)
        target_include_directories(web_server_test PRIVATE tests/circle_network_fake
            tests/circle_guest_fake tests/circle_audio_fake platform/circle/pi500_guest)
        target_compile_features(web_server_test PRIVATE cxx_std_17)
        target_compile_options(web_server_test PRIVATE -Wall -Wextra -Wpedantic)
        add_test(NAME web_server COMMAND web_server_test)
        add_executable(becker_circle_contract_test
            tests/becker_circle_contract_test.cpp
            platform/xroar_proof/becker_circle.cpp)
        target_include_directories(becker_circle_contract_test PRIVATE
            tests/circle_network_fake tests/circle_guest_fake tests/circle_audio_fake)
        target_compile_features(becker_circle_contract_test PRIVATE cxx_std_17)
        target_compile_options(becker_circle_contract_test PRIVATE -Wall -Wextra -Wpedantic)
        add_test(NAME becker_circle_contract COMMAND becker_circle_contract_test)
        add_test(NAME becker_fujinet_frames COMMAND becker_circle_contract_test fujinet)
        # Binary filesystem fixture only, never source/ROM payload. Configure
        # creates a sparse 64 MiB image in this build directory.
        set(storage_image "${CMAKE_CURRENT_BINARY_DIR}/guest-storage-test.img")
        execute_process(COMMAND truncate -s 64M "${storage_image}" COMMAND_ERROR_IS_FATAL ANY)
        add_test(NAME guest_storage COMMAND ${CMAKE_COMMAND}
            "-DTEST_IMAGE=${storage_image}" "-DMKFS_FAT=${MKFS_FAT}"
            "-DTEST_EXE=$<TARGET_FILE:guest_storage_test>"
            -P "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_guest_storage.cmake")
        set(storage_rpi4_image "${CMAKE_CURRENT_BINARY_DIR}/guest-storage-rpi4-test.img")
        execute_process(COMMAND truncate -s 64M "${storage_rpi4_image}" COMMAND_ERROR_IS_FATAL ANY)
        add_test(NAME guest_storage_rpi4 COMMAND ${CMAKE_COMMAND}
            "-DTEST_IMAGE=${storage_rpi4_image}" "-DMKFS_FAT=${MKFS_FAT}"
            "-DTEST_EXE=$<TARGET_FILE:guest_storage_rpi4_test>"
            -P "${CMAKE_CURRENT_SOURCE_DIR}/tests/run_guest_storage.cmake")
    endif()
endif()
