在 VTS 測試期間,shell 指令會用於執行目標端測試二進位檔,以便取得/設定屬性、環境變數和系統資訊,以及啟動/停止 Android 架構。您可以使用 adb shell
指令或在裝置上執行的 VTS 殼層驅動程式 (建議) 執行 VTS 裝置殼層指令。
使用 ADB 殼層
在測試期間需要關閉 USB 連接埠或重新啟動裝置的測試必須使用 ADB 殼層,因為在沒有持續 USB 連線的情況下,VTS 殼層驅動程式無法使用。您可以透過 Python 測試指令碼中的 AndroidDevice
物件叫用 ADB 殼層。例如:
- 取得 Android 裝置物件:
self.device = self.android_devices[0]
- 發出單一殼層指令:
result = self.device.adb.shell(‘ls')
使用 VTS 殼層驅動程式
VTS shell 驅動程式是裝置上執行的代理程式二進位檔,可執行 shell 指令。根據預設,如果驅動程式在裝置上執行,VTS 會使用殼層驅動程式,因為這個方法的延遲時間比使用 adb
shell
指令時短。
VTS 架構支援多裝置測試,在這種測試中,每部 Android 裝置都會在基礎執行器中以 AndroidDevice 物件表示。根據預設,VTS 架構會將 VTS 代理程式和 VTS 殼層驅動程式二進位檔推送至每部 Android 裝置,並在這些裝置上建立與 VTS 代理程式的 TCP 連線。
如要執行殼層指令,主機端 Python 指令碼會對 AndroidDevice 物件中的 ShellMirror 物件發出函式呼叫。ShellMirror 物件會將殼層指令文字打包成 protobuf 訊息,並透過 TCP 通道傳送至 Android 裝置上的 VTS 代理程式。接著,在裝置上執行的代理程式會透過 Unix 套接字,將殼層指令轉送至 VTS 殼層驅動程式。
當 VTS 殼層驅動程式收到殼層指令時,會透過 nohup 在裝置殼層上執行指令,以免發生當機情形。接著,系統會從 nohup
擷取 stdout、stderr 和傳回碼,並傳回 VTS 代理程式。最後,代理程式會將指令結果包裝成 protobuf
訊息,回覆主機。
優點
使用 VTS 殼層驅動程式而非 adb
shell
的好處包括:
- 可靠性:VTS 殼層驅動程式會使用
nohup
在預設設定下執行指令。由於 VTS 測試大多是較低層級的 HAL 和核心測試,nohup
可確保殼層指令不會在執行期間停止運作。 - 效能。雖然
adb shell
指令會快取部分結果 (例如列出目錄中的檔案),但在執行測試二進位檔等工作時,會有連線額外負擔。VTS 殼層驅動程式會在整個測試期間維持有效連線,因此唯一的開銷就是 USB 通訊。在我們的測試中,使用 VTS shell 驅動程式執行包含 100 次對空白 gtest 二進位檔呼叫的命令,比使用adb shell
快上約 20%;由於 VTS shell 通訊會產生大量記錄,實際差異會更大。 - 狀態維護:VTS 殼層驅動程式會為每個終端機名稱維護一個終端機工作階段 (預設終端機名稱為「預設」)。在一個終端機工作階段中設定的環境變數,僅適用於同一個工作階段中的後續指令。
- 可擴充。VTS 架構和裝置驅動程式之間的殼層指令通訊會以 protobuf 包裝,以便日後啟用可能的壓縮、遠端連線、加密等功能。您也可以透過其他方式改善效能,例如在通訊額外負擔大於結果字串剖析時,使用裝置端的結果剖析。
缺點
使用 VTS 殼層驅動程式而非 adb
shell
的缺點包括:
- 其他二進位檔。測試執行後,必須將 VTS 服務專員檔案推送至裝置並清理。
- 必須連上網路。如果主機和代理程式之間的 TCP 連線在測試期間遺失 (因 USB 連線中斷、埠關閉、裝置當機等原因),則無法將殼層指令傳送至 VTS 代理程式。即使自動切換至
adb shell
,系統仍無法得知中斷連線前的指令結果和狀態。
範例
在 VTS 主機端 Python 測試指令碼中使用殼層指令的範例:
- 取得 Android 裝置物件:
self.device = self.android_devices[0]
- 取得所選裝置的殼層物件:
self.shell = self.device.shell
- 發出單一殼層指令:
results = self.shell.Execute(‘ls')
- 發出殼層指令清單:
results = self.shell.Execute([‘cd /data/local/tmp', ‘ls'])
指令結果物件
執行殼層指令後傳回的物件是字典,其中包含 stdouts
、stderrs
和 return_codes
鍵。無論殼層指令是以單一字串或指令字串清單的形式提供,結果字典的每個值一律為清單。
如要驗證指令清單的傳回碼,測試指令碼必須檢查索引。例子:
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')