跳到主要内容

Axvisor 测试

Axvisor 复用了与 StarryOS 测试 相同的测试基础设施(用例发现、资产准备、结果判定),因为两者都是完整 OS/Hypervisor 级别的测试,需要在 rootfs 用户空间中执行测试命令。六种 pipeline 类型(plain/grouped/C/sh/python/rust)的处理逻辑完全相同。

测试编排(用例发现、分组构建、资产准备、结果判定)由 scripts/axbuild/src/test/ 提供统一框架,核心原则是 OS 只构建一次,逐 case 运行。共享框架的完整说明见 测试基础设施;本文描述 Axvisor 特有的测试目录结构、三种测试模式(QEMU / U-Boot / Board)的差异,以及 Axvisor 独有的 test uboot 模式。

1. 测试接口

Axvisor 将 QEMU、U-Boot 和板卡测试置于同一入口下,但每种模式选择不同的运行资产和结果判定路径。命令参数如下,test uboot 是三套系统中唯一的 U-Boot 测试接口。

cargo xtask axvisor test qemu [--test-group <g>] [--test-case <c>] [--list]
cargo xtask axvisor test uboot --board <type> [--guest <image>] [--uboot-config <cfg>] # Axvisor 独有
cargo xtask axvisor test board --board <type> --server <h> --port <p> [--test-case <c>] [--list]

2. 用例发现

Axvisor 测试资产位于:

test-suit/axvisor/
└── normal/
└── <case>/
└── qemu-{arch}.toml

与 StarryOS 的平铺结构不同,Axvisor 用 normal 测试组目录组织用例。发现算法统一通过 build-{target}.toml 定位构建组、qemu-{arch}.toml 定位用例。

3. 运行模式

三种模式共享 case 发现和构建组概念,但宿主环境、启动链路及筛选参数不同。下表用于在 CI 或板端故障时选择正确的复现入口。

模式命令运行环境适用场景
test qemucargo xtask axvisor test qemuQEMU 虚拟机常规功能验证(CI 主力)
test ubootcargo xtask axvisor test ubootAxvisor 独有远程板卡 + U-Boot 引导验证 hypervisor 在真实硬件 + U-Boot 链路上的行为
test boardcargo xtask axvisor test board远程板卡板级回归

3.1 QEMU 测试

最常用的测试模式,在 QEMU 中启动 Axvisor 和配置的 Guest VM。执行链位于 axvisor/test/qemu.rs::test_qemu(),采用两阶段策略:先编译所有 build group,再运行所有 QEMU 用例。

两阶段设计(Phase 1 / Phase 2)的动机:先暴露所有编译错误,避免在 QEMU 上浪费时间后才发现某个 build group 无法编译。

关键步骤的源码行为:

步骤源码位置行为
用例发现discovery.rs::discover_qemu_cases()扫描 test-suit/axvisor/<group>/,默认 group 为 normal
VM 配置qemu_group_build_context()读取 axbuild 已解析并写入 AXVISOR_VM_CONFIGS 的 VM 配置路径
rootfs 准备rootfs::ensure_qemu_rootfs_ready()每个 build group 编译前准备当前 arch 的 managed rootfs
grouped 校验validate_grouped_qemu_commands()检查 test_commands 无空命令
结果判定QemuTestSummary收集所有 case 的 pass/fail,最终 finish_with_total_detail() 统一判定退出码

单个 case 运行(run_qemu_caseload_qemu_case_config):注入 grouped runner(marker 前缀 AXVISOR)、apply_timeout_scale、准备 rootfs 资产(走共享 test/case/ 层)、以 RootfsWritePolicy::Discard patch rootfs 路径。Axvisor 不启用 backtrace capture(capture_backtrace = None)。

3.2 U-Boot 测试

Axvisor 是唯一支持 U-Boot 测试模式的子系统。cargo xtask axvisor test uboot --board <TYPE> 在远程板卡上通过 U-Boot 引导 Axvisor 和 Guest。执行链位于 axvisor/test/board.rs::test_uboot()

参数说明
--board <TYPE>(必需)ostool-server 上的板卡类型
--guest <IMAGE>指定 guest 镜像(默认 linux
--uboot-config <CFG>U-Boot 配置文件,省略时自动发现

关键步骤:

  • 用例定位discover_uboot_test_group() 按 board 名和 guest 名定位唯一的 board test group。
  • U-Boot config 合并merge_board_test_uboot_config() 把 base config(来自 --uboot-config 或自动发现)与 board test config(来自 board-test-*.toml)合并。合并策略:board test 的 success_regexfail_regexuboot_cmdshell_prefixshell_init_cmd 覆盖 base;地址类字段(kernel_load_addrfit_load_addrbootm_addr)仅在 board test 提供时覆盖;base 的 local(串口、波特率)和 dtb_file 保留
  • 编译与运行app.uboot() 一次性完成编译和 U-Boot 运行,由合并后的 U-Boot config 判定结果。

该模式验证完整的"U-Boot → Axvisor → Guest"引导链路,覆盖真实硬件上 U-Boot 加载 Axvisor ELF、Axvisor 初始化硬件虚拟化扩展、再启动 Guest 的全流程。

3.3 板卡测试

板级测试通过 board-{board_name}.toml 配置文件定义。执行链位于 axvisor/test/board.rs::test_board(),使用 BoardTestRunState 逐 group 运行并收集结果(与 QEMU 测试的 QemuTestSummary 类似)。

每个 board test group 的处理:

  1. prepare_request() 加载 build config(SnapshotPersistence::Discard
  2. load_cargo_config() 装配 Cargo
  3. load_board_config() 加载 board test config(board-test-*.toml
  4. app.board() 编译 + 部署到远程板卡,由 board config 的正则判定结果

--test-case--board 支持按用例名和板卡名过滤;--list 列出所有 board test group。发现算法通过 discover_board_test_groups() 递归扫描,board 配置按板卡名命名(board-{name}.toml),通过 nearest_build_wrapper() 向上查找最近的构建配置。

ROCK 4D 用例从板卡文件系统加载 BSP kernel 和 guest DTB,运行前必须单独准备这两项持久化资产。完整命令见 ROCK 4D Linux Guest

4. 资产管线

Axvisor 测试的六种 pipeline 类型与 StarryOS 完全一致,因为两者都需要在 rootfs 用户空间中执行测试命令。resolve_case_pipeline() 按固定优先级检测每个用例目录的特征文件,同一目录同时出现多个 pipeline 触发条件会直接报错:

Pipeline触发条件Axvisor 使用情况
Groupedtest_commands 非空多命令聚合 case
Cc/ 子目录C 测试程序
Shellsh/ 子目录shell 脚本测试
Pythonpython/ 子目录Python 测试
Rustrust/ 子目录(须含 Cargo.tomlRust 测试程序(交叉编译为 musl 静态二进制)
Plain以上均不满足最常见,纯 QEMU 启动验证

pipeline 类型、检测优先级、资产准备、rootfs 缓存和 grouped runner 协议的完整说明见 测试基础设施。Axvisor 的 prepare_staging_root 钩子为空操作(|_| Ok(())),不做 StarryOS 那样的 DNS 注入和 APK 区域配置。

5. x86 嵌套 OVMF/ACPI 验证

normal 组提供两条对称的 x86 嵌套 OVMF 用例:

cargo xtask axvisor test qemu --arch x86_64 --test-group normal \
--test-case ovmf-acpi-vmx
cargo xtask axvisor test qemu --arch x86_64 --test-group normal \
--test-case ovmf-acpi-svm

两者共用 test-suit/axvisor/normal/qemu-acpi-ovmf/x86-linux-acpi-ovmf.toml、同一 BusyBox initramfs 和同一 4 MiB guest firmware 输出。build config 不选择 vmxsvm Cargo feature;唯一的 backend 差异是外层 QEMU 的 -cpu 能力:VMX 暴露 EPT、unrestricted guest 和 flexpriority,SVM 暴露 SVM、NPT 和 NRIP save。因此应分别在 Intel/VMX 与 AMD/SVM KVM 宿主上运行对应 case。

两个 case 均已加入 CI 运行:ovmf-acpi-vmx 在 self-hosted Intel/KVM 宿主上运行(见 .github/workflows/ci.yml 中 "Test axvisor x86_64 ACPI direct and OVMF boot (vmx)" 任务),ovmf-acpi-svm 在 self-hosted AMD/KVM 宿主上运行(见 "Test axvisor self-hosted x86_64 (svm smoke + ACPI)" 任务)。

资产准备复用 Ostool 的 x86_64 OVMF 缓存,可先用以下命令确认来源路径:

cargo xtask ovmf --arch x86_64

Axvisor 测试构建日志会输出本次实际使用的 Ostool CODE、VARS 和最终 guest image 的路径、字节数及 SHA-256,同时说明布局是 split CODE/VARS 还是 monolithic CODE。对于 monolithic CODE,VARS 仍会记录来源证据,但明确标记为 unused;这些运行时摘要不固化到仓库。

成功条件 AXVISOR_X86_OVMF_ACPI_PASSED 来自嵌套 Linux initramfs,而不是 QEMU 外层 OVMF。该 marker 只有在 OVMF 完成 kernel handoff、Linux 进入早期用户态,并且 Linux 能读取 DSDT、APIC、FACP、SPCR、发现 ttyS0、初始化 IOAPIC 且 online CPU 集合为 0 时才会输出。因此它同时证明当前 firmware handoff 和 guest-side ACPI 接受路径。

当前用例仍由 fw_cfg 提供 Linux kernel、initramfs 和命令行。它不证明 OVMF 已枚举 Axvisor guest PCI 启动盘,也不证明 Linux 经 guest ESP 或 EFI stub 启动。