设备 shell 命令

在 VTS 测试期间,shell 命令用于执行目标端测试二进制文件,获取/设置属性、环境变量和系统信息以及启动/停止 Android 框架。您可以使用 adb shell 命令或设备上运行的 VTS shell 驱动程序(推荐本方式)执行 VTS 设备 shell 命令。

使用 ADB shell

如果在测试期间需要关闭 USB 端口或重新启动设备,则必须使用 ADB shell,因为如果没有持久的 USB 连接,VTS shell 驱动程序将不可用。您可以从 Python 测试脚本中的 AndroidDevice 对象调用 ADB shell。例如:

  • 获取 Android 设备对象:
    self.device = self.android_devices[0]
    
  • 发出一个 shell 命令:
    result = self.device.adb.shell(‘ls')
    

使用 VTS shell 驱动程序

VTS shell 驱动程序是一个代理二进制文件,该文件会在设备上运行并执行 shell 命令。默认情况下,如果 shell 驱动程序在设备上运行,则 VTS 将使用该驱动程序,因为此方法比使用 adb shell 命令的延迟更短。

图 1. VTS shell 驱动程序。

VTS 框架支持多设备测试,其中每部 Android 设备都在基础运行器中显示为一个 AndroidDevice 对象。默认情况下,VTS 框架会将 VTS 代理和 VTS shell 驱动程序二进制文件推送到每部 Android 设备,并与这些设备上的 VTS 代理建立 TCP 连接。

要执行 shell 命令,主机端 Python 脚本会对 AndroidDevice 对象内的 ShellMirror 对象发出函数调用。ShellMirror 对象将 shell 命令文本打包成 protobuf 消息,并通过 TCP 通道将其发送至 Android 设备上的 VTS 代理。然后,设备上运行的代理通过 Unix 套接字将 shell 命令转发给 VTS shell 驱动程序。

当 VTS shell 驱动程序收到一个 shell 命令时,它会在设备 shell 上通过 nohup 执行该命令以防止中断。然后从 nohup 检索 stdout、stderr 和返回代码并将其发送回 VTS 代理。最后,代理通过将命令结果封装成 protobuf 消息来回复主机。

优点

使用 VTS shell 驱动程序(而不是 adb shell)的优点包括:

  • 可靠性。 VTS shell 驱动程序使用 nohup 执行命令(采用默认设置)。由于 VTS 测试主要是较低级别的 HAL 和内核测试,因此 nohup 确保 shell 命令在执行期间不会中断。
  • 性能。虽然 adb shell 命令会缓存一些结果(例如列出目录中的文件),但在执行任务(例如执行测试二进制文件)时,它会产生连接开销。VTS shell 驱动程序在整个测试过程中保持有效连接,所以唯一的开销是 USB 通信。在我们的测试中,使用 VTS shell 驱动程序执行一个具有 100 个空 Gtest 二进制文件调用的命令比使用 adb shell 快 20%。由于 VTS shell 通信需要完成大量日志记录工作,因此实际差异更大。
  • 状态保持。VTS shell 驱动程序为每个终端名称(默认终端名称为“default”)保持一个终端会话。在某个终端会话中设置的环境变量仅适用于同一会话中的后续命令。
  • 可扩展性。VTS 框架和设备驱动程序之间的 shell 命令通信封装在 protobuf 中,以便日后能够进行压缩、远程控制、加密等。还存在其他提高性能的可能性,包括通信开销大于结果字符串解析时的设备端结果解析。

缺点

使用 VTS shell 驱动程序(而不是 adb shell)的缺点包括:

  • 额外的二进制文件。VTS 代理文件必须推送到设备并在测试执行之后清理干净。
  • 需要有效连接。如果主机和代理之间的 TCP 连接在测试期间有意或无意地断开(由于 USB 连接断开、端口关闭、设备崩溃等),则 shell 命令无法传输到 VTS 代理。即使自动切换到 adb shell,断开连接之前的命令结果和状态也是未知的。

示例

在 VTS 主机端 Python 测试脚本中使用 shell 命令的示例:

  • 获取 Android 设备对象:
    self.device = self.android_devices[0]
    
  • 为所选设备获取 shell 对象:
    self.shell = self.device.shell
    
  • 发出一个 shell 命令:
    results = self.shell.Execute(‘ls')
    
  • 发出一串 shell 命令:
    results = self.shell.Execute([‘cd /data/local/tmp', ‘ls'])
    

命令结果对象

执行 shell 命令后的返回对象是一个包含键 stdoutsstderrsreturn_codes 的字典。无论 shell 命令是作为单个字符串还是命令字符串列表提供,结果字典的每个值都始终是一个列表。

要验证命令列表的返回代码,测试脚本必须检查索引。例如:

asserts.assertFalse(any(results[‘return_codes']), ‘some command failed.')

或者,脚本可以单独检查每个命令索引。例如:

asserts.assertEqual(results[‘return_codes'][0], 0, ‘first command failed')
asserts.assertEqual(results[‘return_codes'][1], 0, ‘second command failed')