ここでは、テスト マッピングの概略を紹介し、Android オープンソース プロジェクト(AOSP)で簡単にテスト設定を始める方法について説明します。
テスト マッピングについて
テスト マッピングは Gerrit ベースの手法であり、デベロッパーは presubmit および postsubmit のテストルールを Android ソースツリーで直接作成し、テストするブランチとデバイスの決定をテスト インフラストラクチャ自体に任せることができます。テスト マッピング定義は、任意のソース ディレクトリに配置できる TEST_MAPPING という JSON ファイルです。
ATEST は TEST_MAPPING ファイルを使用して、関連付けられたディレクトリで送信前テストを実行できます。テスト マッピングを使用すると、Android ソースツリー内で簡単な変更を行うだけで、同じテストセットを presubmit チェックに追加できます。
次の例をご覧ください。
services.core の presubmit テストを TEST_MAPPING に追加する
imports を使用した tools/dexter の presubmit テストを TEST_MAPPING に追加する
テスト マッピングによるテスト実行と結果レポートは、Trade Federation(TF)テストハーネスを利用しています。
テストグループを定義する
テスト マッピングは、テストグループによってテストをグループ化します。テストグループの名前には任意の文字列を使用できます。たとえば、presubmit は変更の検証時に実行するテストグループです。また、postsubmit は、変更を統合した後にビルドを検証するテストです。
ビルド スクリプト ルールをパッケージ化する
Trade Federation Test Harness で指定ビルドに対してテスト マッピングのテスト モジュールを実行するには、モジュールで、Soong の場合は test_suite を、Make の場合は LOCAL_COMPATIBILITY_SUITE を次の 2 つのテストスイートのいずれかに設定する必要があります。
- general-tests - デバイス固有の機能に依存しないテスト(ほとんどのデバイスにはないベンダー固有のハードウェアなど)。ほとんどのテストは、1 種類の ABI やビットに固有のものや、ハードウェア機能(ABI ごとに異なる test_suite ターゲットがある HWASan など)に固有のものであったり、デバイスで実行する必要があったりしても、general-tests スイートに配置する必要があります。
- device-tests - デバイス固有の機能に依存するテスト。通常、この種のテストは
vendor/
の下に配置されます。「デバイス固有」は、他のデバイスにその ABIまたは SoC 機能があるかどうかを基準とした「固有」ではなく、機能がそのデバイスに「固有」のものであることのみを意味するため、これは、GTest ネイティブ テスト(通常、ABI 固有であってもgeneral-tests
)と同様に、各ビットごとに JUnit テストに適用されます。
例:
Android.bp: test_suites: ["general-tests"],
Android.mk: LOCAL_COMPATIBILITY_SUITE := general-tests
テストスイートで実行するようにテストを設定する
テストスイート内で実行するテストの要件は、次のとおりです。
- ビルド プロバイダが指定されていない。
- テスト中に生成された一時ファイルを削除するなどして、テスト完了後にクリーンアップする必要がある。
- システム設定をデフォルト値または元の値に変更する。
- 特定の状態(ルートの準備完了など)のデバイスを想定していない。ほとんどのテストの実行に root 権限は不要です。テストでルートが必要な場合は、次の例のように
AndroidTest.xml
のRootTargetPreparer
で指定する必要があります。
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
テスト マッピング ファイルを作成する
テスト用のカバレッジを必要とするディレクトリの場合、以下の例のような TEST_MAPPING JSON ファイルを追加します。こうしたルールを使用すると、対象ディレクトリやサブディレクトリ内の任意のファイルにアクセスしたときに、presubmit チェックでテストを実行できます。
例を参照する
TEST_MAPPING
ファイルの例を示します(JSON 形式ですが、コメントがサポートされています)。
{
"presubmit": [
// JUnit test with options and file patterns.
{
"name": "CtsWindowManagerDeviceTestCases",
"options": [
{
"include-annotation": "android.platform.test.annotations.RequiresDevice"
}
],
"file_patterns": ["(/|^)Window[^/]*\\.java", "(/|^)Activity[^/]*\\.java"]
},
// Device-side GTest with options.
{
"name" : "hello_world_test",
"options": [
{
"native-test-flag": "\"servicename1 servicename2\""
},
{
"native-test-timeout": "6000"
}
]
}
// Host-side GTest.
{
"name" : "net_test_avrcp",
"host" : true
}
],
"postsubmit": [
{
"name": "CtsWindowManagerDeviceTestCases"
}
],
"imports": [
{
"path": "frameworks/base/services/core/java/com/android/server/am"
}
]
}
属性を設定する
上記の例の presubmit
と postsubmit
は、各テストグループの名前です。テストグループの詳細については、テストグループの定義をご覧ください。
テスト モジュールの名前や Trade Federation 統合テスト名(uiautomator-uiautomator-demo など、テスト XML ファイルへのリソースパス)は、name
属性の値で設定できます。[名前] 欄では、クラス name
またはテストメソッド name
を使用できません。実行するテストを絞り込む場合は、ここで include-filter
などのオプションを使用できます。include-filter の使用例をご覧ください。
テストの host 設定は、テストがホストで実行されるデバイスレス テストであるかどうかを示します。デフォルト値は false で、この場合はテストを実行するデバイスが必要です。サポートされているテストの種類は、GTest バイナリの場合は HostGTest、JUnit テストの場合は HostTest です。
file_patterns 属性を使用すると、任意のソースコード ファイルの相対パス(TEST_MAPPING ファイルを含むディレクトリへの相対パス)に一致する正規表現文字列のリストを設定できます。上記の例でテスト CtsWindowManagerDeviceTestCases
は、TEST_MAPPING ファイルと同じディレクトリまたはそのサブディレクトリに存在し、ファイル名が Window または Activity で始まる Java ファイルが変更されている場合にのみ presubmit で実行されます。JSON ファイル内での設定では、バックスラッシュ \ はエスケープする必要があります。
imports 属性を使用すると、コンテンツをコピーせずにテストを他の TEST_MAPPING ファイルに含めることができます。インポートされたパスの親ディレクトリにある TEST_MAPPING ファイルも含まれることに留意してください。テスト マッピングでは、インポートのネストが可能です。つまり、2 つの TEST_MAPPING ファイルが互いをインポートでき、テスト マッピングに含まれるテストを適切に統合できます。
options 属性には、追加の TradeFed コマンドライン オプションが含まれます。
特定のテストで使用可能なオプションの全リストを取得するには、次のコマンドを実行します。
tradefed.sh run commandAndExit [test_module] --help
オプションの仕組みについて詳しくは、TradeFed オプションの処理をご覧ください。
Atest を使用してテストを実行する
presubmit テストルールをローカルで実行するには:
- TEST_MAPPING ファイルが含まれているディレクトリに移動します。
- 次のコマンドを実行します。
atest
現在のディレクトリと親ディレクトリの TEST_MAPPING ファイルで設定されているすべての presubmit テストが実行されます。Atest は、presubmit の 2 つのテスト(A と B)を見つけて実行します。
これは、現在の作業ディレクトリ(CWD)と親ディレクトリにある TEST_MAPPING ファイルで presubmit テストを実行する最も簡単な方法です。Atest は CWD とすべての親ディレクトリで TEST_MAPPING ファイルを見つけて使用します。
ソースコードを構成する
次の例は、ソースツリー全体で TEST_MAPPING ファイルを構成する方法を示しています。
src
├── project_1
│ └── TEST_MAPPING
├── project_2
│ └── TEST_MAPPING
└── TEST_MAPPING
src/TEST_MAPPING
のコンテンツ:
{
"presubmit": [
{
"name": "A"
}
]
}
src/project_1/TEST_MAPPING
のコンテンツ:
{
"presubmit": [
{
"name": "B"
}
],
"postsubmit": [
{
"name": "C"
}
],
"other_group": [
{
"name": "X"
}
]}
src/project_2/TEST_MAPPING
のコンテンツ:
{
"presubmit": [
{
"name": "D"
}
],
"import": [
{
"path": "src/project_1"
}
]}
ターゲット ディレクトリを指定する
ターゲット ディレクトリを指定して、そのディレクトリ内の TEST_MAPPING ファイルでテストを実行できます。次のコマンドは、2 つのテスト(A、B)を実行します。
atest --test-mapping src/project_1
postsubmit テストルールを実行する
このコマンドを使用して、src_path
(デフォルトは CWD)と親ディレクトリの TEST_MAPPING で定義されている postsubmit テストルールを実行することもできます。
atest [--test-mapping] [src_path]:postsubmit
デバイスを必要としないテストのみを実行する
Atest の --host オプションを使用して、デバイスを必要としないホストに対して構成されたテストのみを実行できます。このオプションを指定しない場合、Atest はデバイスを必要とするテスト、ホスト上で実行されデバイスを必要としないテストの両方を実行します。テストは、2 つの独立したスイートで実行されます。
atest [--test-mapping] --host
テストグループを識別する
テストグループは、Atest コマンドで指定できます。次のコマンドは、src/project_1 ディレクトリにあるファイルに関連付けられた postsubmit テストをすべて実行します。このディレクトリには、1 つのテスト(C)のみが含まれています。
または、:all を使用すると、グループに関係なくすべてのテストを実行できます。次のコマンドは、4 つのテスト(A、B、C、X)を実行します。
atest --test-mapping src/project_1:all
サブディレクトリを含める
デフォルトでは、Atest を使用して TEST_MAPPING 内のテストを実行すると、CWD(または指定ディレクトリ)と親ディレクトリの TEST_MAPPING ファイルで構成された presubmit テストのみが実行されます。サブディレクトリ内のすべての TEST_MAPPING ファイルでテストを実行するには、--include-subdir
オプションを使用して Atest に強制的にこれらのテストを追加します。
atest --include-subdir
--include-subdir
オプションを指定しない場合、Atest はテスト A のみを実行します。--include-subdir
オプションを指定すると、Atest は 2 つのテスト(A、B)を実行します。
行レベルのコメントのサポート
行レベルの //
形式のコメントを追加して、以下のように TEST_MAPPING ファイルに設定の説明を追加できます。ATest と Trade Federation は TEST_MAPPING を前処理して、コメントがない有効な JSON 形式にします。JSON ファイルの明瞭さと判読性を保持するため、行レベルの //
形式のコメントのみがサポートされています。
例:
{
// For presubmit test group.
"presubmit": [
{
// Run test on module A.
"name": "A"
}
]
}