1. はじめに
今回は、Zephyr 公式に採用されている Test Runner である Twister について紹介していきます。
Twister は事前に定義したテストを、複数のボード・設定に対してビルドから実行・結果判定まで自動で回す Test Runner で、Github Actions や git hooks などを利用して変更が加えられた際に検証を行う仕組みを提供しています。
本記事では、手動でビルド実行を行うための設定について紹介していきます。自動化については必要に応じて Github Actions や git hooks について別途調べて連携させてください。
Zephyr 公式の Twister についての解説は以下に記載されています。
2. Twister の役割
1. はじめに でも触れたように、Twister はあくまで Test Runner、つまりテストを駆動させるのが主な役割となります。
テスト内容そのものは、ZTest として別途提供されており、ZTest で想定内の入出力を設定し、その検証を実行し、その結果を検証・判定するのが Twister の役割となります。
この記事ではまずはその骨組みについて解説していきます。
3. Twister の設定ファイル
Twister が検出する設定ファイル名は、testcase.yaml と sample.yaml です(ただし sample.yaml非推奨)。
このファイルをテスト駆動させたいディレクトリに配置させておくことで、後述する west twister ... コマンドを使って動作させることができます。
yaml 内では大まかに sample:, common:, tests: の3つのセクションが利用できます。次章でそれぞれの役割・記述内容を説明していきます。
2026.04.12 の変更で tests.yaml も対象になり、zephyr/sample 以下のサンプルコードも sample.yaml から tests.yaml に置き換えられました。(次の v4.5.0 から適用される?)
*.yaml は OK だとして、*.yml はどうなんだ?と考えてしまいますが、*.yml は検出できないようです。
166 TEST_DEFINITION_FILENAME = [
167 'testcase.yaml',
168 'tests.yaml',
169 'sample.yaml'
170 ]
3.1. sample セクション
sample という名前ですが、特段 sample 専用というわけではなく、そのアプリの名称と説明を記述する項目となっています。
実際の定義は下記のように、name: と description: が利用できます。
sample:
type: object
properties:
name:
type: string
description:
type: string
required: [name]
additionalProperties: false
3.2. common / tests セクション
common: は汎用的に使いたい・関数のように複数ヶ所から参照したい場合に定義するためのセクションで、
一方 tests: は具体的に実行したいテスト内容を登録するセクションです。
common: に記述せず tests: にすべての設定を記述することも可能ですが、common: に一旦テスト内容を書いた上で、tests: からそれを参照するのがスマートです。
それぞれのセクションで利用できる項目は結構多いので、確認したい場合は以下を開いて参照してください。
実際の定義
$defs:
scenario:
type: object
properties:
arch_exclude: {}
arch_allow: {}
vendor_exclude:
type: array
items:
type: string
vendor_allow:
type: array
items:
type: string
testcases:
type: array
items:
type: string
build_only:
type: boolean
build_on_all:
type: boolean
depends_on: {}
extra_args: {}
extra_configs:
type: array
items:
type: string
extra_conf_files:
type: array
items:
type: string
extra_overlay_confs:
type: array
items:
type: string
extra_dtc_overlay_files:
type: array
items:
type: string
extra_sections: {}
expect_reboot:
type: boolean
build:
type: boolean
required_applications:
type: array
items:
type: object
properties:
application:
type: string
name:
# deprecated: use 'application' instead
type: string
path:
type: string
platform:
type: string
anyOf:
- required: [application]
- required: [name]
additionalProperties: false
required_snippets:
type: array
items:
type: string
filter:
type: string
levels:
type: array
items:
type: string
enum: ["smoke", "unit", "integration", "acceptance", "system", "regression"]
integration_platforms:
type: array
items:
type: string
integration_toolchains:
type: array
items:
type: string
ignore_faults:
type: boolean
ignore_qemu_crash:
type: boolean
harness:
type: string
sidecar:
type: string
sidecar_config:
# Per-sidecar configuration, namespaced by the sidecar name so several
# sidecars can define keys without colliding; only the block matching
# the scenario's `sidecar:` value is consumed. The concrete per-sidecar
# properties are filled in at load time from the registered sidecars
# (see twisterlib.sidecars.sidecar_config_schema), so a new sidecar does
# not have to edit this file. This placeholder validates the shape for
# any consumer that loads the schema directly.
type: object
harness_config:
type: object
properties:
power_measurements: {}
shell_commands_file:
type: string
shell_commands:
type: array
items:
type: object
properties:
command:
type: string
expected:
type: string
required: [command]
additionalProperties: false
display_capture_config:
type: string
type:
type: string
fixture: {}
ordered:
type: boolean
pytest_root:
type: array
items:
type: string
pytest_args:
type: array
items:
type: string
pytest_dut_scope:
type: string
enum: ["function", "class", "module", "package", "session"]
required_devices:
type: array
items:
type: object
properties:
platform:
type: string
fixture:
type: array
items:
type: string
application:
type: string
path:
type: string
additionalProperties: false
ctest_args:
type: array
items:
type: string
regex:
type: array
items:
type: string
robot_testsuite: {}
robot_option: {}
record:
type: object
properties:
regex:
type: array
items:
type: string
merge:
type: boolean
as_json:
type: array
items:
type: string
required: [regex]
additionalProperties: false
bsim_exe_name:
type: string
tests_scripts:
type: array
items:
type: string
ztest_suite_repeat:
type: integer
ztest_test_repeat:
type: integer
ztest_test_shuffle:
type: boolean
additionalProperties: false
min_ram:
type: integer
min_flash:
type: integer
modules:
type: array
items:
type: string
platform_exclude: {}
platform_allow: {}
platform_type:
type: array
items:
type: string
enum: ["mcu", "qemu", "sim", "unit", "native"]
platform_key:
type: array
items:
type: string
simulation_exclude:
type: array
items:
type: string
enum:
- qemu
- simics
- xt-sim
- renode
- nsim
- mdb-nsim
- tsim
- armfvp
- native
- custom
tags: {}
timeout:
type: integer
toolchain_exclude: {}
toolchain_allow: {}
type:
type: string
enum: ["unit"]
skip:
type: boolean
slow:
type: boolean
sysbuild:
type: boolean
additionalProperties: false
3.3. hello_world の設定
では、実際に hello_world に配置されている tests.yaml を確認してみます。
sample:
description: Hello World sample, the simplest Zephyr
application
name: hello world
common:
min_ram: 2
min_flash: 16
tags: introduction
integration_platforms:
- native_sim
harness: console
harness_config:
type: one_line
regex:
- "Hello World! (.*)"
tests:
sample.basic.helloworld:
tags: introduction
-
sample:は説明したとおり、その概要とアプリケーション名を記述しています。記述は任意です。 -
tests:は項目に対してタグ付けしているだけで、動作に影響を与える項目はありません。その結果common:に定義した内容がそのまま適用されます。tests:は必須項目です。 -
common:hello_worldでは以下の項目が定義されています。scenario:を使うことで複数登録でき、twister 実行時に内容を指定することもできます。なおcommon:に記述した内容はtests:側に直接書くこともでき、記述は任意です。
| 項目 | 説明 |
|---|---|
| min_ram | RAM が最低 2KB ある MCU を対象に設定 |
| min_flash | ROM が最低 16KB ある MCU を対象に設定 |
| tags | この項目に対してタグ付けしています。twister 実行時に |
| platform_allow |
hello_world には無いですが、ここで指定したターゲットボードを検証対象と設定可能。rpi_pico などを追記可能 |
| integration_platforms | ターゲット指定の絞り込み。native_sim は Linux アプリケーションとして動作する指定 |
| harness | テスト結果の判定方法を指定。console は Linux アプリとして動作させた上で出力される文字列を判定することになります。その他 test, console, pytest, shell, gtest, ctest, robot, power, display_capture が指定できるようです |
| harness_config |
harness が console 指定だった場合に利用できる、判定方法の指定。正規表現で成否判定を行う |
4. 実行
実行方法は、west コマンドが使える状態で以下のコマンドで実施します。-T には検査対象のコードや設定ファイルが配置されたディレクトリを指定します。
west twister -T zephyr/samples/hello_world/
$ west twister -T zephyr/samples/hello_world/
INFO - Using Ninja..
INFO - Zephyr version: v4.4.0
INFO - Using 'zephyr/gnu' toolchain variant.
INFO - Selecting default platforms per testsuite scenario
INFO - Building initial testsuite list...
INFO - Built testsuite list in 0.00 seconds
INFO - Writing JSON report /home/taka/Projects/zephyr/official/zephyr/samples/hello_world/twister-out/testplan.json
INFO - JOBS: 16
INFO - Adding tasks to the queue...
INFO - Added initial list of jobs to queue
INFO - Total complete: 30/ 30 100% built (not run): 0, filtered: 18, failed: 0, error: 0
INFO - 1 test scenarios (48 configurations) selected, 18 configurations filtered (18 by static filter, 0 at runtime).
INFO - 30 of 30 executed test configurations passed (100.00%), 0 built (not run), 0 failed, 0 errored, with no warnings in 56.41 seconds.
INFO - 30 of 30 executed test cases passed (100.00%) on 30 out of total 1473 platforms (2.04%).
INFO - 30 test configurations executed on platforms, 0 test configurations were only built.
INFO - Saving reports...
INFO - Writing JSON report /home/taka/Projects/zephyr/official/zephyr/samples/hello_world/twister-out/twister.json
INFO - Writing xunit report /home/taka/Projects/zephyr/official/zephyr/samples/hello_world/twister-out/twister.xml...
INFO - Writing xunit report /home/taka/Projects/zephyr/official/zephyr/samples/hello_world/twister-out/twister_report.xml...
INFO - Run completed
実行が終わると twister-out というディレクトリに結果がファイルとして出力されます。
$ ls zephyr/samples/hello_world/twister-out/
mps2_an385 qemu_or1k_qemu_or1k
mps2_an386 qemu_riscv32_qemu_virt_riscv32
mps2_an521_cpu0 qemu_riscv32_qemu_virt_riscv32_smp
mps3_corstone300_an547 qemu_riscv32e_qemu_virt_riscv32e
native_sim_native qemu_riscv64_qemu_virt_riscv64
qemu_arc_qemu_arc_em qemu_riscv64_qemu_virt_riscv64_smp
qemu_arc_qemu_arc_hs qemu_rx_r5f562n8
qemu_arc_qemu_arc_hs5x qemu_x86_64_atom
qemu_arc_qemu_arc_hs6x qemu_x86_atom
qemu_arc_qemu_arc_hs_xip qemu_xtensa_dc233c
qemu_cortex_a53_qemu_cortex_a53 qemu_xtensa_dc233c_mmu
qemu_cortex_a53_qemu_cortex_a53_smp qemu_xtensa_sample_controller32_mpu
qemu_cortex_a9_xc7z007s testplan.json
qemu_cortex_m0_nrf51822 twister.json
qemu_cortex_r5_zynqmp_rpu twister.log
qemu_leon3_leon3 twister.xml
qemu_malta_qemu_malta twister_report.xml
qemu_malta_qemu_malta_be twister_suite_report.xml
5. まとめ
今回、Zephyr が採用・提供している Test Runner である Twister について紹介しました。
これ単体でもビルドが通るのか、期待した printk() が出力されるのか等の検証は可能ですが、さらに Zephyr が提供している ZTest を用いることで、より詳細に検証が行えるようになります。
この ZTest についてはまた別途投稿していきたいと思います。