| <html devsite><head> |
| <title>多设备测试</title> |
| <meta name="project_path" value="/_project.yaml"/> |
| <meta name="book_path" value="/_book.yaml"/> |
| </head> |
| <body> |
| <!-- |
| Copyright 2018 The Android Open Source Project |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <p>VTS 支持需要在多部 Android 设备之间进行交互的测试。</p> |
| |
| <h2 id="architecture">架构</h2> |
| <p>VTS 使用 TradeFed 框架获取设备序列号并将其传递给测试模块。</p> |
| |
| <p><img src="images/vts_device_serials.png"/></p> |
| <figcaption><strong>图 1.</strong> VTS 对设备序列号进行传递的过程。</figcaption> |
| |
| <p>设备要求(如设备数量和设备类型)在测试计划配置中指定。例如,您可以在测试计划中指定需要两部具有 Sailfish 编译目标的 Android 设备。</p> |
| |
| <h3 id="device-allocation">设备分配</h3> |
| <p>测试基础架构(通常是测试调度程序)将满足测试计划配置中指定要求的可用设备分配给 VTS 框架。即使测试模块没有使用分配的设备,这些设备仍会保留用于测试计划。然后将 VTS 代理二进制文件推送到所有已分配设备上,并在设备上运行(除非有不要运行的特殊指示)。这可确保用于 shell 命令和 HAL RPC 的 TCP 连接可用于测试脚本中的所有设备。</p> |
| |
| <h3 id="test-preparers">测试准备器</h3> |
| <p>框架会为所收到的序列号对应的所有设备运行测试准备器。目标准备器可以是单设备准备器或多设备准备器:</p> |
| <ul> |
| <li>单设备目标准备器(<a href="https://android.googlesource.com/platform/test/vts/+/master/harnesses/tradefed/src/com/android/tradefed/targetprep/VtsDeviceInfoCollector.java" class="external">VtsDeviceInfoCollector</a> 上的示例):<ul> |
| <li>只能在测试计划配置中根据所需的设备列表指定(未来版本将允许在模块配置中指定)。</li> |
| <li>只接收一个设备序列号。</li> |
| <li>针对特定设备运行准备和清理任务。</li> |
| </ul> |
| </li> |
| <li>多设备目标准备器(<a href="https://android.googlesource.com/platform/test/vts/+/master/harnesses/tradefed/src/com/android/tradefed/targetprep/VtsPythonVirtualenvPreparer.java" class="external">VtsPythonVirtualenvPreparer</a> 上的示例):<ul> |
| <li>可以在测试计划配置或测试模块配置中指定</li> |
| <li>接收所有设备序列号</li> |
| <li>为每部设备或所有设备运行准备和清理任务。</li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h3 id="test-modules">测试模块</h3> |
| <p>在测试准备器设置完主机/设备后,测试模块会获取设备列表。系统会针对每个多设备测试模块运行一个主机端 Python 测试模块。分配的 Android 设备可以从 Python 测试模块(以 <a href="https://android.googlesource.com/platform/test/vts/+/master/utils/python/controllers/android_device.py#322" class="external">AndroidDevice</a> 对象列表形式)访问:</p> |
| <pre class="devsite-click-to-copy"> |
| devices = self.android_devices |
| device1 = devices[0] |
| device1_serial = device1.serial |
| </pre> |
| |
| <p>即使测试计划中的测试模块仅使用了一部设备,所有已分配的设备都会保留用于测试计划。</p> |
| |
| <h2 id="device-communication">测试期间的设备通信</h2> |
| <p>有效的多 Android 设备测试需要在已分配设备之间进行通信。在开发此类测试时,您必须确定如何在已分配设备之间建立通信。以下部分提供了三个通信示例(但是,测试开发者可以自由设计其他模型)。</p> |
| |
| <h3 id="type1">类型 1:主机端 HAL 测试</h3> |
| <p>主机端 HAL 测试可以使用默认推送到设备的 VTS HAL 驱动程序:</p> |
| |
| <p><img src="images/vts_hostside_hal.png"/></p> |
| <figcaption><strong>图 2.</strong> 主机端 HAL 测试。</figcaption> |
| |
| <p>在这种情况下:</p> |
| <ul> |
| <li>测试逻辑在主机上执行。</li> |
| <li>主机端测试脚本向每部设备上的驱动程序发出 RPC 调用。</li> |
| <li>主机端协调设备的交互。</li> |
| </ul> |
| |
| <h3 id="type2">类型 2:基于代理的主机端测试</h3> |
| <p>除了在设备上使用 VTS 代理外,主机端测试还可以将自己的代理(应用或二进制文件)推送到每部设备:</p> |
| |
| <p><img src="images/vts_hostside_agent.png"/></p> |
| <figcaption><strong>图 3.</strong> 基于代理的主机端测试。</figcaption> |
| |
| <p>在这种情况下:</p> |
| <ul> |
| <li>测试逻辑在主机上执行。</li> |
| <li>代理应用(或二进制文件)安装在每部设备上。</li> |
| <li>主机端测试脚本向每部设备上的应用发出命令。</li> |
| <li>主机端协调设备的交互。</li> |
| </ul> |
| |
| <p>例如,当前 VTS repo 中的<a href="https://android.googlesource.com/platform/test/vts-testcase/nbu/+/master" class="external">下一批十亿用户测试</a>是基于应用的主机端多设备测试。</p> |
| |
| <h3 id="type3">类型 3:目标端 HIDL 测试</h3> |
| <p>目标端、多设备 HIDL 测试将所有测试逻辑置于设备端测试二进制文件中,这要求测试在测试执行期间同步设备:</p> |
| |
| <p><img src="images/vts_target_hidl.png"/></p> |
| <figcaption><strong>图 4. </strong>基于目标的 HIDL 测试。</figcaption> |
| |
| <p>在这种情况下:</p> |
| <ul> |
| <li>在设备上执行测试逻辑。</li> |
| <li>主机端框架提供了初始设备标识。</li> |
| <li>目标端测试二进制文件需要同步:<ul> |
| <li>所有设备使用的测试二进制文件相同。</li> |
| <li>每个角色使用的测试二进制文件不同。</li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h2 id="example-multi-device">示例:多设备测试计划</h2> |
| <p>此示例为两部设备指定了配置:</p> |
| <ul> |
| <li>设备 1 包括一个编译版本提供程序和 <code>VtsDeviceInfoCollector</code> 目标准备器。</li> |
| <li>设备 2 包括一个额外的 <code>FilePusher</code> 准备器,可将一组主机驱动的相关文件推送至设备。</li> |
| </ul> |
| |
| <pre class="devsite-click-to-copy"> |
| <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> |
| </pre> |
| |
| <h2 id="example-hostside">示例:主机端 Python 测试脚本</h2> |
| <p>有关测试准备器的详细信息和示例,请参阅<a href="#Test-preparers">测试准备器</a>。有关完整的主机端多设备示例,请参阅 <a href="https://android.googlesource.com/platform/test/vts/+/master/testcases/codelab/hello_world_multi" class="external">hello_world_multi codelab</a>。 |
| </p> |
| |
| <pre class="devsite-click-to-copy"> |
| 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') |
| </pre> |
| |
| </body></html> |