多设备测试

VTS 支持需要在多部 Android 设备之间进行交互的测试。

架构

VTS 使用 TradeFed 框架获取设备序列号并将其传递给测试模块。

图 1. VTS 对设备序列号进行传递的过程。

设备要求(如设备数量和设备类型)在测试计划配置中指定。例如,您可以在测试计划中指定需要两部具有 Sailfish 编译目标的 Android 设备。

设备分配

测试基础架构(通常是测试调度程序)将满足测试计划配置中指定要求的可用设备分配给 VTS 框架。即使测试模块没有使用分配的设备,这些设备仍会保留用于测试计划。然后将 VTS 代理二进制文件推送到所有已分配设备上,并在设备上运行(除非有不要运行的特殊指示)。这可确保用于 shell 命令和 HAL RPC 的 TCP 连接可用于测试脚本中的所有设备。

测试准备器

框架会为所收到的序列号对应的所有设备运行测试准备器。目标准备器可以是单设备准备器或多设备准备器:

  • 单设备目标准备器(VtsDeviceInfoCollector 上的示例):
    • 只能在测试计划配置中根据所需的设备列表指定(未来版本将允许在模块配置中指定)。
    • 只接收一个设备序列号。
    • 针对特定设备运行准备和清理任务。
  • 多设备目标准备器(VtsPythonVirtualenvPreparer 上的示例):
    • 可以在测试计划配置或测试模块配置中指定
    • 接收所有设备序列号
    • 为每部设备或所有设备运行准备和清理任务。

测试模块

在测试准备器设置完主机/设备后,测试模块会获取设备列表。系统会针对每个多设备测试模块运行一个主机端 Python 测试模块。分配的 Android 设备可以从 Python 测试模块(以 AndroidDevice 对象列表形式)访问:

devices = self.android_devices
device1 = devices[0]
device1_serial = device1.serial

即使测试计划中的测试模块仅使用了一部设备,所有已分配的设备都会保留用于测试计划。

测试期间的设备通信

有效的多 Android 设备测试需要在已分配设备之间进行通信。在开发此类测试时,您必须确定如何在已分配设备之间建立通信。以下部分提供了三个通信示例(但是,测试开发者可以自由设计其他模型)。

类型 1:主机端 HAL 测试

主机端 HAL 测试可以使用默认推送到设备的 VTS HAL 驱动程序:

图 2. 主机端 HAL 测试。

在这种情况下:

  • 测试逻辑在主机上执行。
  • 主机端测试脚本向每部设备上的驱动程序发出 RPC 调用。
  • 主机端协调设备的交互。

类型 2:基于代理的主机端测试

除了在设备上使用 VTS 代理外,主机端测试还可以将自己的代理(应用或二进制文件)推送到每部设备:

图 3. 基于代理的主机端测试。

在这种情况下:

  • 测试逻辑在主机上执行。
  • 代理应用(或二进制文件)安装在每部设备上。
  • 主机端测试脚本向每部设备上的应用发出命令。
  • 主机端协调设备的交互。

例如,当前 VTS repo 中的下一批十亿用户测试是基于应用的主机端多设备测试。

类型 3:目标端 HIDL 测试

目标端、多设备 HIDL 测试将所有测试逻辑置于设备端测试二进制文件中,这要求测试在测试执行期间同步设备:

图 4. 基于目标的 HIDL 测试。

在这种情况下:

  • 在设备上执行测试逻辑。
  • 主机端框架提供初始设备标识。
  • 目标端测试二进制文件需要同步:
    • 所有设备使用的测试二进制文件相同。
    • 每个角色使用的测试二进制文件不同。

示例:多设备测试计划

此示例为两部设备指定了配置:

  • 设备 1 包括一个 build 提供程序和 VtsDeviceInfoCollector 目标准备器。
  • 设备 2 包括一个额外的 FilePusher 准备器,可将一组主机驱动的相关文件推送至设备。
<configuration description="VTS Codelab Plan">
  ...
<device name="device1">
<build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
<target_preparer class="com.android.tradefed.targetprep.VtsDeviceInfoCollector" />
</device>
<device name="device2" >
<build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
<target_preparer class="com.android.tradefed.targetprep.VtsDeviceInfoCollector" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
<option name="push-group" value="HostDrivenTest.push" />
</target_preparer>
</device>
<option name="compatibility:include-filter" value="VtsCodelabHelloWorldMultiDeviceTest" />
</configuration>

示例:主机端 Python 测试脚本

有关测试准备器的详细信息和示例,请参阅测试准备器。有关完整的主机端多设备示例,请参阅 hello_world_multi codelab

def setUpClass(self):
logging.info('number of device: %s', self.android_devices)
asserts.assertEqual(len(self.android_devices), 2, 'number of device is wrong.')
self.dut1 = self.android_devices[0]
self.dut2 = self.android_devices[1]
self.shell1 = self.dut1.shell
self.shell2 = self.dut2.shell

def testSerialNotEqual(self):
'''Checks serial number from two device not being equal.'''
command = 'getprop | grep ro.serial'
res1 = self.shell1.Execute(command)
res2 = self.shell2.Execute(command)

def getSerialFromShellOutput(output):
'''Get serial from getprop query'''
return output[const.STDOUT][0].strip().split(' ')[-1][1:-1]
serial1 = getSerialFromShellOutput(res1)
serial2 = getSerialFromShellOutput(res2)

logging.info('Serial number of device 1 shell output: %s', serial1)
logging.info('Serial number of device 2 shell output: %s', serial2)
asserts.assertNotEqual(serial1, serial2, 'serials from two devices should not be the same')
asserts.assertEqual(serial1, self.dut1.serial, 'serial got from device system property is different from allocated serial')
asserts.assertEqual(serial2, self.dut2.serial, 'serial got from device system property is different from allocated serial')