自 2025 年 3 月 27 日起,我們建議您使用 android-latest-release
而非 aosp-main
建構及貢獻 AOSP。詳情請參閱「Android 開放原始碼計畫變更」。
編寫分割的 IRemoteTest 測試執行程式
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
編寫測試執行程式時,請務必考量擴充性。請問自己:「如果測試執行程式必須執行 20 萬個測試案例,需要多久時間?」
分割是 Trade Federation 提供的答案之一。這項操作需要將執行程式需要的所有測試分割成可並行執行的多個區塊。
本頁說明如何讓執行程式可供 Tradefed 分割。
要實作的介面
如要讓 TF 將您的模型視為可分割,您必須實作最重要的介面 IShardableTest,其中包含 split(int numShard)
和 split()
這兩種方法。
如果區塊劃分作業取決於要求的區塊數量,您應實作 split(int numShard)
。否則,請實作 split()
。
當 TF 測試指令搭配使用區隔參數 --shard-count
和 --shard-index
執行時,TF 會逐一檢查所有 IRemoteTest
,尋找實作 IShardableTest
的 IRemoteTest
。如果找到,就會呼叫 split
取得新的 IRemoteTest
物件,為特定分割區執行部分測試案例。
請問我應該瞭解哪些關於分割導入方式的資訊?
- 您的執行程式只能在某些條件下分割;在這種情況下,如果您未分割,請傳回
null
。
- 盡可能分割:將執行程式分割成適當的執行單元。這取決於執行程式。例如:HostTest 是在類別層級進行分割,因此每個測試類別都會放入個別的分割區。
- 如果有需要,請新增一些選項來稍微控制分割作業。例如:AndroidJUnitTest 有
ajur-max-shard
,可指定可分割的切片數量上限,無論要求的數量為何。
詳細實作範例
以下是實作 IShardableTest
的程式碼片段範例,供您參考。完整程式碼可在 (https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/test_framework/com/android/tradefed/testtype/InstalledInstrumentationsTest.java) 取得
/**
* Runs all instrumentation found on current device.
*/
@OptionClass(alias = "installed-instrumentation")
public class InstalledInstrumentationsTest
implements IDeviceTest, IResumableTest, IShardableTest {
...
/** {@inheritDoc} */
@Override
public Collection<IRemoteTest> split(int shardCountHint) {
if (shardCountHint > 1) {
Collection<IRemoteTest> shards = new ArrayList<>(shardCountHint);
for (int index = 0; index < shardCountHint; index++) {
shards.add(getTestShard(shardCountHint, index));
}
return shards;
}
// Nothing to shard
return null;
}
private IRemoteTest getTestShard(int shardCount, int shardIndex) {
InstalledInstrumentationsTest shard = new InstalledInstrumentationsTest();
try {
OptionCopier.copyOptions(this, shard);
} catch (ConfigurationException e) {
CLog.e("failed to copy instrumentation options: %s", e.getMessage());
}
shard.mShardIndex = shardIndex;
shard.mTotalShards = shardCount;
return shard;
}
...
}
這個範例只會建立自身的新例項,並將區塊參數設為該例項。不過,分割邏輯可能會因測試而異,只要是確定性的邏輯,且能產生一組完整的子集,就沒問題。
獨立
分割區必須獨立!在執行程式中實作 split
時建立的兩個區塊,不應彼此依賴或共用資源。
分割區塊必須是確定性的!這也是必要條件,在相同條件下,split
方法應一律傳回完全相同的切片清單,且順序也相同。
注意:由於每個分片都能在不同的 TF 例項上執行,因此務必確保 split
邏輯產生的子集是互斥的,且以確定的方式共同涵蓋所有子集。
在本機分割測試
如要在本機 TF 上分割測試,只要在指令列中加入 --shard-count
選項即可。
tf >run host --class com.android.tradefed.UnitTests --shard-count 3
接著,TF 會自動為每個分割區產生指令並執行。
tf >l i
Command Id Exec Time Device State
3 0m:03 [null-device-2] running stub on build 0 (shard 1 of 3)
3 0m:03 [null-device-1] running stub on build 0 (shard 0 of 3)
3 0m:03 [null-device-3] running stub on build 0 (shard 2 of 3)
匯總測試結果
由於 TF 不會為分割呼叫執行任何測試結果匯總作業,因此您必須確保報表服務支援這項作業。
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-27 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-07-27 (世界標準時間)。"],[],[],null,["# Write a sharded IRemoteTest test runner\n\nWhen writing a test runner, it's important to think about scalability. Ask\nyourself, \"if my test runner had to run 200K test cases\" how long would it take?\n\nSharding is one of the answers available in Trade Federation. It requires\nsplitting all the tests the runner needs into several chunks that can be\nparallelized.\n\nThis page describes how to make your runner shardable for Tradefed.\n\nInterface to implement\n----------------------\n\nThe single most important interface to implement to be considered shardable by\nTF is\n[IShardableTest](https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/src/com/android/tradefed/testtype/IShardableTest.java),\nwhich contains two methods: `split(int numShard)` and `split()`.\n\nIf your sharding is going to depend on the number of shards requested, you\nshould implement `split(int numShard)`. Otherwise, implement `split()`.\n\nWhen a TF test command is executed with sharding parameters `--shard-count` and\n`--shard-index`, TF iterates through all `IRemoteTest` to look for ones\nimplementing `IShardableTest`. If found, it will call `split` to\nget a new `IRemoteTest` object to run a subset of test cases for a specific\nshard.\n\nWhat should I know about the split implementation?\n--------------------------------------------------\n\n- You runner may shard upon some conditions only; in that case return `null` when you did not shard.\n- Try to split as much as it makes sense: split your runner into unit of execution that makes sense for it. It really depends of your runner. For example: [HostTest](https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/test_framework/com/android/tradefed/testtype/HostTest.java) is sharded at the Class level, each test class is put in a separate shard.\n- If it makes sense, add some options to control the sharding a little bit. For example: [AndroidJUnitTest](https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/test_framework/com/android/tradefed/testtype/AndroidJUnitTest.java) has a `ajur-max-shard` to specify the maximum number of shards it could split in, regardless of the number requested.\n\nDetailed example implementation\n-------------------------------\n\nHere is an example code snippet implementing `IShardableTest` you can\nreference. The full code is available at\n(https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/test_framework/com/android/tradefed/testtype/InstalledInstrumentationsTest.java) \n\n /**\n * Runs all instrumentation found on current device.\n */\n @OptionClass(alias = \"installed-instrumentation\")\n public class InstalledInstrumentationsTest\n implements IDeviceTest, IResumableTest, IShardableTest {\n ...\n\n /** {@inheritDoc} */\n @Override\n public Collection\u003cIRemoteTest\u003e split(int shardCountHint) {\n if (shardCountHint \u003e 1) {\n Collection\u003cIRemoteTest\u003e shards = new ArrayList\u003c\u003e(shardCountHint);\n for (int index = 0; index \u003c shardCountHint; index++) {\n shards.add(getTestShard(shardCountHint, index));\n }\n return shards;\n }\n // Nothing to shard\n return null;\n }\n\n private IRemoteTest getTestShard(int shardCount, int shardIndex) {\n InstalledInstrumentationsTest shard = new InstalledInstrumentationsTest();\n try {\n OptionCopier.copyOptions(this, shard);\n } catch (ConfigurationException e) {\n CLog.e(\"failed to copy instrumentation options: %s\", e.getMessage());\n }\n shard.mShardIndex = shardIndex;\n shard.mTotalShards = shardCount;\n return shard;\n }\n ...\n }\n\nThis example simply creates a new instance of itself and sets shard\nparameters to it. However, the splitting logic can be totally different from\ntest to test; and as long as it is deterministic and yields collectively\nexhaustive subsets, it is okay.\n\nIndependence\n------------\n\nShards need to be independent! Two shards created by your implementation of\n`split` in your runner should not have dependencies on each other or share\nresources.\n\nShards splitting needs to be deterministic! This is also mandatory, given the\nsame conditions, your `split` method should always return the exact same list of\nshards in the same order.\n\nNOTE: Since each shard can run on different TF instances, it is critical to\nensure the `split` logic yields subsets that are mutually exclusive and\ncollectively exhaustive in a deterministic manner.\n\nShard a test locally\n--------------------\n\nTo shard a test on a local TF, you can simply add the `--shard-count` option to\nthe command line. \n\n tf \u003erun host --class com.android.tradefed.UnitTests --shard-count 3\n\nThen TF will automatically spawn commands for each shard and run them. \n\n tf \u003el i\n Command Id Exec Time Device State\n 3 0m:03 [null-device-2] running stub on build 0 (shard 1 of 3)\n 3 0m:03 [null-device-1] running stub on build 0 (shard 0 of 3)\n 3 0m:03 [null-device-3] running stub on build 0 (shard 2 of 3)\n\nTest result aggregation\n-----------------------\n\nSince TF does not do any test result aggregation for sharded invocations, you\nneed to make sure your reporting service supports it."]]