第19章 测试与仿真环境搭建
说实话,做车载OTA开发这几年,我踩过最大的坑就是——在真车上调试。有一次,一个简单的升级包解析错误,我抱着笔记本在测试车里蹲了整整一下午。车里的空调还坏了,那滋味...后来我学乖了,先把仿真环境搭得妥妥当当,再上真车。
这一章,我就把压箱底的环境搭建经验掏出来。从QEMU模拟器到Cuttlefish虚拟设备,再到HIL台架和Tradefed自动化框架,咱们一个一个说清楚。
核心观点:仿真环境不是替代品,而是加速器。好的仿真环境能覆盖80%的OTA测试场景,剩下20%留给真车验证。
19.1 QEMU模拟器配置
QEMU这东西,说白了就是一个硬件模拟器。你可以在PC上模拟出一台完整的ARM设备,跑Android系统。我刚开始用的时候,总觉得它慢,后来发现是配置没调对。
19.1.1 基础环境准备
先说说硬件要求。QEMU跑Android,CPU最好支持硬件虚拟化。Intel的VT-x或者AMD的AMD-V,都得在BIOS里打开。内存至少16GB,我建议32GB起步。磁盘空间?准备100GB吧,AOSP编译产物就占不少地方。
# 安装QEMU和相关工具
sudo apt-get install qemu-system-arm qemu-efi-aarch64
sudo apt-get install libvirt-daemon-system virt-manager
# 下载预编译的Android系统镜像
wget https://ci.android.com/builds/latest/branches/aosp-main/artifacts/aosp_cf_arm64_phone-userdebug/view/qemu-img.zip
unzip qemu-img.zip -d ~/android-qemu
19.1.2 启动脚本配置
我个人习惯写一个启动脚本,把参数都封装好。这样每次启动不用敲一长串命令。
#!/bin/bash
# start-qemu.sh - 启动Android QEMU模拟器
QEMU_SYSTEM=qemu-system-aarch64
KERNEL=~/android-qemu/kernel
RAMDISK=~/android-qemu/ramdisk.img
SYSTEM_IMG=~/android-qemu/system.img
USERDATA_IMG=~/android-qemu/userdata.img
$QEMU_SYSTEM \
-machine virt,virtualization=true,gic-version=3 \
-cpu cortex-a72 \
-smp 4 \
-m 4096 \
-kernel $KERNEL \
-initrd $RAMDISK \
-append "console=ttyAMA0 androidboot.hardware=qemu" \
-drive file=$SYSTEM_IMG,if=none,id=system,format=raw \
-device virtio-blk-device,drive=system \
-drive file=$USERDATA_IMG,if=none,id=userdata,format=raw \
-device virtio-blk-device,drive=userdata \
-netdev user,id=net0,hostfwd=tcp::5555-:5555 \
-device virtio-net-device,netdev=net0 \
-nographic
小技巧:加上-nographic参数后,模拟器不启动图形界面,只通过终端交互。这对CI/CD环境特别有用。我在做自动化OTA测试时,都是这么跑的。
19.1.3 网络桥接配置
OTA测试离不开网络。QEMU默认的user模式网络,说白了就是个NAT,模拟器能上网,但外部连不进来。我们需要桥接模式。
# 创建桥接网络
sudo brctl addbr br0
sudo brctl addif br0 eth0
sudo ip addr add 192.168.1.100/24 dev br0
sudo ip link set br0 up
# QEMU启动时指定桥接网络
-netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
-device virtio-net-device,netdev=net0,mac=52:54:00:12:34:56
嗯,这里要注意。桥接模式需要root权限,而且会改变主机网络配置。我在项目中遇到过,有同事不小心把网络搞断了,整个团队都连不上服务器。建议在专用测试机上操作。
19.2 Cuttlefish虚拟设备
Cuttlefish是Google官方推荐的Android虚拟设备方案。跟QEMU比,它更贴近真机体验。为什么?因为它模拟了完整的硬件抽象层(HAL),包括摄像头、传感器、GPS等。
19.2.1 环境搭建
Cuttlefish需要Linux内核支持KVM,而且对内核版本有要求。我建议用Ubuntu 22.04 LTS,内核5.15以上。
# 安装Cuttlefish依赖
sudo apt-get install -y git devscripts config-package-dev debhelper-compat golang curl
# 下载Cuttlefish源码
git clone https://github.com/google/android-cuttlefish.git
cd android-cuttlefish
# 编译安装
make
sudo make install
# 启动Cuttlefish
sudo launch_cvd --daemon
19.2.2 OTA测试专用配置
Cuttlefish最牛的地方,是支持OTA更新包的直接推送。你想想看,在真机上测试OTA,每次都要刷机、重启、等待。在Cuttlefish上,一条命令搞定。
# 生成OTA包
ota_from_target_files \
-i target_files_old.zip \
-o ota_update.zip \
target_files_new.zip
# 推送到Cuttlefish设备
adb connect 10.0.2.15:6520
adb push ota_update.zip /data/ota_package/
adb shell update_engine_client \
--update \
--payload=file:///data/ota_package/ota_update.zip \
--metadata_size=65536 \
--metadata_signature=... \
--public_key=/etc/update_engine/update-payload-key.pub.pem
注意:Cuttlefish默认使用10.0.2.15这个IP地址。如果你在Docker里跑,需要做端口映射。我曾经因为忘记映射端口,折腾了两个小时才发现问题。
19.3 HIL(硬件在环)测试台架
HIL测试,说白了就是把真实的硬件(比如ECU、T-Box)接入仿真环境。软件跑在模拟器上,但通信走真实硬件。这样既能验证软件逻辑,又能检查硬件兼容性。
19.3.1 台架架构
我画了一张图,帮你理解HIL台架的整体架构:
19.3.2 搭建步骤
搭建HIL台架,我总结了四个步骤:
- 硬件选型:根据测试需求选择ECU型号。我建议用开发板级别的硬件,比如NXP的S32G系列,接口丰富,调试方便。
- 接口适配:把硬件的CAN、以太网、串口等接口,通过USB转接板连接到主机。注意电平匹配,我曾经烧过一个ECU的串口,就是因为没注意3.3V和5V的区别。
- 通信中间件:写一个适配层,把硬件接口抽象成API。比如CAN消息的收发,封装成send_can_frame()和recv_can_frame()。
- 测试编排:用Python或Shell脚本,编排测试流程。比如:启动模拟器→连接ECU→推送OTA包→监控升级状态→验证结果。
# HIL测试脚本示例
#!/usr/bin/env python3
import can
import subprocess
import time
# 初始化CAN接口
bus = can.interface.Bus(channel='can0', bustype='socketcan')
# 启动Cuttlefish
subprocess.run(['launch_cvd', '--daemon'])
# 等待设备就绪
time.sleep(30)
# 推送OTA包
subprocess.run(['adb', 'push', 'ota_update.zip', '/data/ota_package/'])
# 监控升级过程中的CAN消息
def monitor_can():
while True:
msg = bus.recv()
if msg.arbitration_id == 0x100: # OTA状态消息
print(f"OTA状态: {msg.data}")
if msg.data[0] == 0xFF: # 升级完成
break
monitor_can()
19.4 自动化测试框架(Tradefed)
Tradefed是Android官方的自动化测试框架。说实话,刚开始接触的时候,我觉得它挺复杂的。但用顺手之后,发现它确实强大。尤其是做OTA回归测试,Tradefed可以帮你自动跑几百个测试用例。
19.4.1 框架结构
Tradefed的核心概念就三个:
| 组件 | 作用 | 我的理解 |
|---|---|---|
| Test | 单个测试用例 | 就是你要测的那个东西 |
| Device | 测试设备抽象 | 可以是真机、模拟器、Cuttlefish |
| Invocation | 一次测试执行 | 从开始到结束的完整流程 |
19.4.2 OTA测试配置
写一个Tradefed的OTA测试配置,其实不复杂。关键是把升级流程拆解成几个步骤:准备、推送、升级、验证。
<?xml version="1.0" encoding="utf-8"?>
<configuration description="OTA升级测试配置">
<device name="device">
<option name="serial" value="emulator-5554"/>
<option name="type" value="cuttlefish"/>
</device>
<test class="com.android.tradefed.testtype.OtaTest">
<option name="ota-package" value="/data/ota_package/ota_update.zip"/>
<option name="pre-ota-checks" value="check_battery,check_storage"/>
<option name="post-ota-checks" value="check_version,check_apps"/>
<option name="timeout" value="600000"/> <!-- 10分钟超时 -->
</test>
<logger class="com.android.tradefed.log.FileLogger">
<option name="log-level" value="VERBOSE"/>
<option name="output-file" value="/tmp/ota_test_log.txt"/>
</logger>
</configuration>
19.4.3 运行与结果分析
配置写好了,怎么跑?一条命令搞定:
# 运行OTA测试
tradefed run config ota_test.xml
# 查看测试结果
tradefed list results
# 重新运行失败的用例
tradefed run retry --previous-session-id 12345
经验之谈:Tradefed的日志非常详细,但也很啰嗦。我习惯用grep过滤关键信息:grep -E "(FAIL|PASS|ERROR)" test_log.txt。这样一眼就能看出哪些用例没过。
19.5 环境集成与最佳实践
把上面这些环境整合起来,形成一个完整的测试流水线。我画个图帮你理解:
最后,分享几个我踩过的坑:
- 磁盘空间:QEMU和Cuttlefish的镜像文件都很大,一个镜像动辄几个GB。我建议用LVM管理磁盘,方便扩容。
- 网络隔离:测试环境最好跟办公网络隔离。OTA测试会频繁推送大文件,容易占满带宽。我吃过这个亏,整个团队都卡得不行。
- 日志收集:每次测试都要自动收集日志。我写了个脚本,测试结束后自动打包logcat、kernel log、Tradefed报告,存到NAS上。出了问题,直接翻日志,不用再复现。
总结一下:仿真环境搭建,说白了就是「用软件模拟硬件,用自动化替代手动」。QEMU适合快速验证,Cuttlefish适合完整功能测试,HIL台架适合硬件兼容性验证,Tradefed负责把这一切串起来。把这四样玩转了,OTA测试的效率至少提升5倍。