import shutil import subprocess from pathlib import Path import pytest @pytest.mark.parametrize("clock_mhz", [240, 300], ids=["240MHz", "300MHz"]) @pytest.mark.parametrize( ("controller_count", "neutral_input"), [(2, False), (4, False), (2, True), (4, True)], ids=[ "native-management", "native-two-pair", "neutral-one-pair", "neutral-two-pair", ], ) def test_native_hub_management_native( tmp_path: Path, controller_count: int, neutral_input: bool, clock_mhz: int ) -> None: root = Path(__file__).resolve().parents[1] cc = shutil.which("cc") or shutil.which("gcc") cxx = shutil.which("c++") or shutil.which("g++") assert cc is not None and cxx is not None, "host C and C++ compilers are required" includes = [ f"-I{root / 'tests' / 'native_hub_stubs'}", f"-I{root / 'tests' / 'bluepad32_native_stubs'}", f"-I{root / 'src' / 'firmware'}", f"-I{root / 'tools' / 'pico_usb_address_probe'}", f"-I{root / 'tools' / 'switch2_usb_probe'}", ] flags = [ "-Wall", "-Wextra", "-Werror", "-pedantic", "-ffunction-sections", "-fdata-sections", "-DSWITCH2_PROBE_HUB=1", f"-DPROBE_CONTROLLER_COUNT={controller_count}", f"-DSWITCH_PICO_SYS_CLOCK_MHZ={clock_mhz}", ] flags.append(f"-DSWITCH2_PROBE_NEUTRAL_INPUT={int(neutral_input)}") if controller_count == 4 and not neutral_input: flags.append("-DSWITCH2_BRIDGE_FULL_INPUT=1") transport = tmp_path / "native_hub_transport.o" executable = tmp_path / "native_hub_management_test" subprocess.run( [ cc, "-std=c11", *flags, *includes, "-c", str(root / "tests" / "native_hub_transport_fixture.c"), "-o", str(transport), ], check=True, cwd=root, ) sources = [ "tests/native_hub_management_test.cpp", "src/firmware/usb/usb_configuration_management.cpp", "tools/switch2_usb_probe/bootsel.cpp", "src/firmware/configuration/adapter_configuration.cpp", "src/firmware/core/controller_identity.cpp", "src/firmware/profile/controller_profile.cpp", "src/firmware/profile/profile_storage.cpp", "src/firmware/profile/profile_service.cpp", ] subprocess.run( [ cxx, "-std=c++17", *flags, *includes, "-Wl,--gc-sections", *(str(root / path) for path in sources), str(transport), "-o", str(executable), ], check=True, cwd=root, ) for reboot_slot in ("root", "child"): subprocess.run([str(executable), reboot_slot], check=True, cwd=root) for trace in (False, True): router_executable = tmp_path / f"native_hub_router_test_{int(trace)}" trace_flags = ["-DSWITCH2_PROBE_TRACE_NATIVE_INPUT=1"] if trace else [] subprocess.run( [ cc, "-std=c11", *flags, *trace_flags, *includes, str(root / "tests" / "native_hub_router_test.c"), "-o", str(router_executable), ], check=True, cwd=root, ) subprocess.run([str(router_executable)], check=True, cwd=root) @pytest.mark.parametrize("clock_mhz", [240, 300], ids=["240MHz", "300MHz"]) @pytest.mark.parametrize("controller_count", [2, 4], ids=["one-pair", "two-pair"]) @pytest.mark.parametrize("trace_enabled", [False, True], ids=["plain", "trace"]) def test_native_hub_cold_startup( tmp_path: Path, controller_count: int, trace_enabled: bool, clock_mhz: int ) -> None: root = Path(__file__).resolve().parents[1] cc = shutil.which("cc") or shutil.which("gcc") assert cc is not None, "a host C compiler is required" executable = tmp_path / "native_hub_startup_test" flags = [ f"-DPROBE_CONTROLLER_COUNT={controller_count}", f"-DSWITCH_PICO_SYS_CLOCK_MHZ={clock_mhz}", ] if trace_enabled: flags.append("-DSWITCH2_PROBE_TRACE_NATIVE_INPUT=1") subprocess.run( [ cc, "-std=c11", "-Wall", "-Wextra", "-Werror", "-pedantic", "-ffunction-sections", "-fdata-sections", "-DSWITCH2_PROBE_HUB=1", *flags, f"-I{root / 'tests' / 'native_hub_stubs'}", f"-I{root / 'src' / 'firmware'}", f"-I{root / 'tools' / 'pico_usb_address_probe'}", f"-I{root / 'tools' / 'switch2_usb_probe'}", str(root / "tests" / "native_hub_startup_test.c"), "-Wl,--gc-sections", "-o", str(executable), ], check=True, cwd=root, ) for scenario in ( "ready", "delayed", "timeout", "mismatched-clock", "unsupported-clock", "inexact-clock", ): subprocess.run([str(executable), scenario], check=True, cwd=root)