23、CTS/VTS 认证:兼容性测试套件、测试用例编写、常见失败修复

做嵌入式Android系统,最头疼的环节之一就是过认证。CTS和VTS,说白了就是Google给你设的两道关卡。CTS管的是应用兼容性,VTS管的是底层驱动和内核。我刚开始带项目时,觉得这玩意儿就是跑个分,后来被坑了几次才明白——不过认证,你的ROM连出厂都别想。

这一章,我就把CTS/VTS的底裤扒干净。从测试套件怎么跑,到用例怎么写,再到那些常见的失败怎么修,咱们一条一条捋清楚。

CTS与VTS:到底测什么?

先搞清楚两个东西的区别。

项目 CTS (Compatibility Test Suite) VTS (Vendor Test Suite)
测试对象 Framework、API、应用行为 HAL、内核、Vendor接口
关注点 App能不能跑、API是否一致 驱动是否稳定、HAL实现是否正确
失败后果 Google Play无法认证 系统稳定性差,可能死机
测试工具 cts-tradefed vts-tradefed

CTS失败,用户装个微信可能闪退。VTS失败,你的摄像头可能打不开。我遇到过最离谱的一次,VTS的Camera HAL测试全挂,查了两天发现是HIDL接口里少传了一个参数。

测试套件怎么跑?

跑测试其实不复杂,但环境配置容易踩坑。我个人习惯用Linux环境,Ubuntu 20.04最稳。

先下载套件:

# 从AOSP源码编译
source build/envsetup.sh
lunch aosp_arm64-userdebug
make cts -j16
make vts -j16

# 或者直接下载预编译包
wget https://dl.google.com/dl/android/cts/android-cts-12.0_r1-linux_x86-arm.zip
unzip android-cts-12.0_r1-linux_x86-arm.zip

然后连接设备,开始跑:

# 进入CTS目录
cd android-cts/tools
./cts-tradefed

# 跑全量测试
run cts --plan CTS

# 跑某个模块
run cts --module CtsCameraTestCases

# 跑单个用例
run cts --module CtsCameraTestCases --test android.camera.cts.CameraTest#testPreview

VTS的跑法类似,只是命令换成vts-tradefed。嗯,这里要注意:跑VTS前,设备必须解锁bootloader,并且刷入userdebug版本。我刚开始用user版本跑,结果一堆测试直接跳过,浪费了半天。

测试用例编写:从零开始写一个CTS用例

有时候官方用例覆盖不全,或者你加了自定义硬件,就得自己写。写CTS用例说白了就是写一个Android JUnit测试,然后加上CTS的注解。

举个例子,假设你加了一个自定义传感器,需要验证它的数据范围:

package android.cts.mydevice;

import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.CtsTest;

@CtsTest
public class MySensorTest extends AndroidTestCase {

    private SensorManager mSensorManager;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mSensorManager = (SensorManager) getContext()
                .getSystemService(Context.SENSOR_SERVICE);
    }

    public void testSensorRange() {
        Sensor sensor = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        assertNotNull("Accelerometer not found", sensor);

        float maxRange = sensor.getMaximumRange();
        // 我遇到过传感器返回0的情况,所以加个边界检查
        assertTrue("Range should be positive", maxRange > 0);
        assertTrue("Range too small", maxRange > 19.6f); // 2g
    }
}

写完之后,编译成APK,放到CTS的testcases目录下,然后通过tradefed跑。说白了,Google的CTS框架就是一个超级JUnit Runner,你只要遵循它的规则就行。

我的小技巧: 写用例时,尽量用assertTrue而不是assertEquals。因为CTS的日志里,assertTrue失败会直接打印表达式,调试起来快很多。

常见失败修复:那些年我踩过的坑

CTS/VTS失败的原因千奇百怪,但归纳起来就几类。我按频率排个序:

1. SELinux策略违规

这是最常见的失败。你加了新驱动,但没给对应的SELinux权限。日志里会看到avc: denied。

修复方法:

  • 先看dmesg或logcat,找到被denied的domain和context
  • 在device/manufacturer/device-name/sepolicy/下添加.te文件
  • 比如:allow hal_sensors_default sensor_device:chr_file rw_file_perms;

我曾经因为少写了一个file_type的声明,导致VTS的Sensor测试全挂。查了两天,最后发现是.te文件里忘了加type_transition。

2. HIDL接口实现不完整

VTS会遍历所有HIDL接口,如果你的HAL没实现某个方法,直接fail。

举个例子,Camera HAL必须实现getCameraIdList,如果你返回了空列表,VTS就报错。

修复方法:

  • 用vts-tradefed的--retry选项,只跑失败的用例
  • 看VTS的详细日志,找到哪个接口没实现
  • 补全HAL代码,重新编译vendor镜像
注意: 有些HIDL接口是optional的,但VTS默认全测。你可以在device.mk里加上TARGET_VTS_SKIP_LIST来跳过,但我不建议这么做——跳过多了,Google审核时会打回来。

3. 资源文件缺失

CTS里有些测试会检查系统资源,比如壁纸、铃声、字体。如果你裁剪了系统,删了默认壁纸,CTS的CtsWallpaperTestCases就会挂。

修复方法:

  • 在device.mk里确保PRODUCT_COPY_FILES包含了必要资源
  • 或者直接继承aosp的通用资源包

4. 时间戳与系统时间问题

VTS里有些测试会检查系统启动时间、休眠时间。如果你的RTC驱动没调好,时间戳对不上,测试就fail。

我遇到过最奇葩的一个:VTS的SystemVendorTest,因为系统时区设成了UTC+8,但测试期望的是UTC,结果差了8小时。最后在build.prop里加了ro.timezone=UTC才过。

知识体系总览

下面这张图,是我自己总结的CTS/VTS认证知识结构。你可以把它当成一个检查清单:

CTS/VTS 认证 CTS 兼容性测试 API 一致性检查 App 行为兼容性 资源文件完整性 SELinux 策略 VTS 供应商测试 HIDL 接口完整性 HAL 实现正确性 内核驱动稳定性 时间戳与休眠 常见失败修复:SELinux / HIDL / 资源 / 时间

避坑指南:我踩过的那些雷

最后,分享几个实战经验。这些坑我当年都踩过,你遇到了直接照着修就行。

  • 跑CTS前一定要关掉动画: 开发者选项里把窗口动画、过渡动画、动画时长都设为0。否则有些UI测试会因为动画没结束而超时失败。
  • VTS测试最好用userdebug版本: user版本会跳过很多需要root权限的测试,导致覆盖率不够。
  • 日志要保留原始文件: CTS/VTS生成的test_result.xml和logcat.txt,别删。Google审核时可能会要求你提供这些文件。
  • 不要一次性改太多: 我习惯每次只修一个失败,然后重新跑那个模块。如果一次性改一堆,出了问题都不知道是哪个改动引起的。

嗯,CTS/VTS认证说白了就是一场耐心战。你只要把SELinux、HIDL、资源文件这三个大头搞定,剩下的都是小问题。我当年第一次过CTS,整整磨了两周,但第二次做新项目时,三天就搞定了——因为踩过的坑都记在本子上了。

公众号:蓝海资料掘金营,微信deep3321