Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マイコンでもRustのtestがしたい その2/KernelVM Tokyo 18
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Toshifumi NISHINAGA
August 09, 2025
Programming
2.6k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
マイコンでもRustのtestがしたい その2/KernelVM Tokyo 18
Toshifumi NISHINAGA
August 09, 2025
More Decks by Toshifumi NISHINAGA
See All by Toshifumi NISHINAGA
マイコンでもRustのtestがしたい/KernelVM Kansai 11
tnishinaga
1
1.8k
BareMetalで遊ぶRaspberry Pi 5 PCIe編/KernelVM Tokyo17
tnishinaga
1
3.6k
probe-rsの紹介と最近の貢献紹介/CELF-02-03
tnishinaga
1
790
SecurityCamp2023基板作るコース講義資料/Security Camp 2023 Lecture Materials
tnishinaga
8
2.8k
RP2040のPIOを使う話/KernelVM Hokuriku 6
tnishinaga
3
2.1k
JTAGでArmプロセッサをデバッグする方法のつづき/KernelVM_Tokyo16
tnishinaga
0
690
CMSIS-DAPの概要と使い方/KernelVM Online5
tnishinaga
1
2.4k
JTAGでarmプロセッサをデバッグする話/KernelVM Online4
tnishinaga
5
3.9k
ARM入門/arm introduction
tnishinaga
14
13k
Other Decks in Programming
See All in Programming
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
12k
さぁV100、メモリをお食べ・・・
nilpe
0
130
New "Type" system on PicoRuby
pocke
1
480
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.3k
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
380
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
140
タクシーアプリ『GO』の バックエンド開発のおける AI利活用と若者のすべて
pyama86
3
1.9k
3Dシーンの圧縮
fadis
1
660
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
140
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
760
AIエージェントと協働するCLI開発 — BunとOpenClawで学んだこと
yoshikouki
1
240
Stage 3 Decorators でできること / できないこと / TSKaigi 2026
susisu
1
1.6k
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
sira's awesome portfolio website redesign presentation
elsirapls
0
270
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
260
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
160
How STYLIGHT went responsive
nonsquared
100
6.2k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.9k
30 Presentation Tips
portentint
PRO
1
320
How GitHub (no longer) Works
holman
316
150k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
190
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
390
The agentic SEO stack - context over prompts
schlessera
0
790
Transcript
マイコンでも Rustのtestがしたい その2 @tnishinaga(CVついなちゃん) 2025/08/09 KernelVM@Tokyo 18 2025/05/11 1
前回のおさらい • マイコン(no_std)でもRustのテストをしたいなら、embedded- testを使うと良い • probe-rs + embedded-test • https://crates.io/crates/embedded-test
• いいところ • std環境のtestとほぼ同じ書き方ができる • panicするテスト(should panic)が書ける 2025/05/11 2
今日の目的 • 大目的 • no_std環境でもstd環境のcargo test並のテストがしたい • 中目的 • std環境のcargo
testがどのような仕組みで動いているかを知る • no_std環境では今最もstd環境に近いtestが実現できるprobe-rs+embedded- testがどうやってその機能を実現しているかを深堀りする • 小目的 • cargo testはどうやってテストの成否を判定しているのか • probe-rsとembedded-testはそれぞれ何をやっているか • なぜshould_panicテストができるのか • custom test harness では使えないのになぜ使えるの? 2025/05/11 3
cargo testはどうやってテスト の成否を判定しているのか 2025/05/11 4
cargo testはどうやってテストの成否を判 定しているのか • embedded-testの詳細を理解するために、まずはcargo testが何 をしているかを知る 2025/05/11 5
std Rustのテスト • unit test • ソースコード内に testコードを追記 するだけ •
integration test • tests/*.rs にテスト を書く • doc test • コードのドキュメ ント内にテストが かける(!?) • 今回は省略 参考: https://doc.rust-lang.org/rust-by- example/testing/integration_testing.html unit test integration test 2025/05/11 6 コード: https://github.com/tnishinaga/20250511_kernelvm_kansai_sample/tree/main/no01_std_tests
cargo test実行の様子 2025/05/11 7
cargo test実行結果 unit test 部分 integration test 部分 結果発表 成功時に
0を返す 2025/05/11 8
cargo testを実行すると何が起こるのか • テスト用バイナリを生成する • rustcにtest用オプション(--test)を渡してビルド(※) • テスト用バイナリを実行する • 個別のテストの成否はここで集計
• report形式の変更等もテスト用バイナリの担当 • テスト用バイナリの終了コードを見てTestの成否を判断 • cargo testは各ユニットテストの実行や集計等には関与していない • テスト用バイナリ(正確にはtest crate) の機能 • 前回のK/VMで紹介した内容は誤り 2025/05/11 9 ※: cargo build --tests --lib -vvv するとコンパイルオプションが取れる
cargo testのコード確認 1. run_tests内で run_unit_testが呼ばれる 2. run_unit_test内ではtest 用バイナリをビルドし てexecしているだけ 2025/05/11
10 https://github.com/rust- lang/cargo/blob/c24e1064277fe51ab7 2011e2612e556ac56addf7/src/cargo/o ps/cargo_test.rs#L76 ① ②
cargo testの動作確認 • 目的 • cargo testがテストの実行と終了コードの確認しかしてないことを確認 する • 方法
• runnerにexit 0を実行するshell scriptを登録してcargo testを実行する • cargoにはrunnerがセットされている場合はrunnerを使ってバイナリを実行する仕 組みがあるので、これを使う • 例: cargo run時にprobe-rsを使ってプログラムを書き込んで実行させるとか • cargo testの実装から読み取った挙動 • 何もテストが実行されていなくてもTestにPassしたと出てくるはず • exit 1に書き換えればFailするはず 2025/05/11 11
動作確認結果 • exit 0を返すだけの shell scriptを動かす とtestが成功する • exit 1を返すと失敗に
なる • よって、cargo testは 終了コードしか見て いない 2025/05/11 12
テスト用バイナリ単体の実行結果確認 • cargo test実行時にビルドされる バイナリを直接実行してみる • テストの集計等をテスト用バイナ リ側がやってるとわかるはず • 実行結果
• テスト用バイナリがテストの実施 ・集計等を担っていた 2025/05/11 13
テスト用バイナリの生成機序 • cargo testがrustcに `--test` オプションを渡してビルドしている • rustcは `--test` オプションを渡されると以下を行う
• cfg(test)を有効化する • https://github.com/rust- lang/rust/blob/6b00bc3880198600130e1cf62b8f8a93494488cc/compiler/rustc_session /src/config/cfg.rs#L295-L298 • testを実行するmain関数を展開 • 多分このあたりでやっている • https://github.com/rust- lang/rust/blob/master/compiler/rustc_builtin_macros/src/test_harness.rs#L287 • cfg(test)よりmod testsが有効化される • mod tests内の `#[test]` マクロが展開され、test実行用の定義が追加 される 2025/05/11 14
テスト用マクロ展開 実 行 ハーネスを生成 (cargo expand --lib --tests) マクロ展開部 生成されたtest
harness 2025/05/11 15
展開されたコードとtest crateがやること • 展開されたコード • 各unit test用関数に対応したTestDescAndFn型の定義を作る • HarnessはTestDescAndFnのsliceをtest_main_static関数に渡して実行する •
libtest側 • 受け取ったsliceを元にunit testの関数を呼び出して順次実行する • 実態はこのあたり • https://github.com/rust- lang/rust/blob/e05ab47e6c418fb2b9faa2eae9a7e70c65c98eaa/library/test/src/lib.rs#L284 • 引数が与えられると各種機能を提供する • 各unit testの一覧表示とか、出力形式の変更とか • IDE側のテストの単体実行などを実現する際に使われている • この機能を実装すると、各種IDE側との親和性が高まる(= libtest compatibleになる) 2025/05/11 16
この節のまとめ • cargo testはテストバイナリのビルドと実行を行っている • テストの実態はtest crateが担っている 2025/05/11 17
probe-rs+embedded-testのテス トの仕組み 2025/05/11 18
前回のおさらい • no_std環境ではstd環境のテストができない • test時に生成されるハーネスがstdに依存しているため • custom test frameworksを使えばno_std環境でもテストはできる が、一部機能が使えない
• panicするテストとか • probe-rs+embedded-testを使えばno_std環境でもstd環境並みの テストができる • panicするテストも書ける • なぜ? 2025/05/11 19
probe-rsを使ったcargo testのOverview 2025/05/11 20 cargo test (1) probe-rs run \
<test options> probe-rs (test mode) マイコン (2) イメージ書き込み (3) 実行 & 結果(json) (4) 成否判定 集計 表示 (5) exit codeを返す (0)テストコードのビルド probe-rsとマイコン間の文字入出力や終了コードのやり取りにはsemihostingを用いる
probe-rsを使ったcargo testのOverview 2025/05/11 21 cargo test (1) probe-rs run \
<test options> probe-rs (test mode) マイコン (2) イメージ書き込み (3) 実行 & 結果(json) (4) 成否判定 集計 表示 (5) exit codeを返す (0)テストコードのビルド probe-rsとマイコン間の文字入出力や終了コードのやり取りにはsemihostingを用いる
マイコンで動くプログラムの動作 • 以下の機能をembedded-test crateが提供している • semihosting経由で受け取った引数に応じて以下を行う • テストのリストを返す(list) • 指定されたテストを実行する(run)
• テストの成否等をsemihosting経由で返す 2025/05/11 22
embedded-test crateの機能 • ビルド時 • マクロ展開をしてno_stdで動くテスト用バイナリを作る • 説明省略 • テストの実行は
embedded_test::export::run_testsで行う • 動作時(embedded_test::export::run_testsの機能) • 引数に応じて以下の2機能を提供する • list • 各テストの一覧をsemihosting経由で出力する • run <テスト名> • 指定されたテストを実行し、成否をsemihostingで返す • 各コマンド実行後は終了する • 複数のテストを実行する機能はない。そこは外部のプログラムだより。 2025/05/11 23
マクロ展開 2025/05/11 24 • #[embedded_test::tests] マクロが以下の展開を行 う • #[init] •
#[test] • #[should_panic] • #[ignore] • #[timeout] • 展開コードはこのあたり • https://github.com/probe- rs/embedded- test/blob/81857b62554196 724ea81d0a9d1020e7bc60 41a0/macros/src/lib.rs#L1 18-L138
マクロ展開されたコードがやっているこ と • 以下の定義を生成 • テスト用main関数 • __embedded_test_entry と、そこから呼ばれる __embedded_test_start
• おもしろポイント • リンカスクリプトに定義されたシンボル経由で__embedded_test_startを呼ぶこと でembedded-test.xの不足をコンパイル時に検出している • 最後に embedded_test::export::run_testsでテストを実行する • 各種テスト用情報の入ったheapless::vec::Vec • 関数名とか関数ポインタとか 2025/05/11 25
embedded_test::export::run_testsの機能 • semihosting経由で引数を受け取って出力を返す • 実行結果(成功・失敗)をsemihosting経由で伝える • 成功時はexit(0)を返す • https://github.com/probe-rs/embedded- test/blob/81857b62554196724ea81d0a9d1020e7bc6041a0/src/export.rs#L84C12-
L84C30 • 失敗時または異常終了時はabortが返る • 失敗時(各テストは check_outcome関数経由で実行され、結果が返る) • https://github.com/probe-rs/embedded- test/blob/81857b62554196724ea81d0a9d1020e7bc6041a0/src/export.rs#L105-L113 • 異常終了時(panic) • https://github.com/probe-rs/embedded- test/blob/81857b62554196724ea81d0a9d1020e7bc6041a0/src/lib.rs#L18 2025/05/11 26
probe-rsを使ったcargo testのOverview 2025/05/11 27 cargo test (1) probe-rs run \
<test options> probe-rs (test mode) マイコン (2) イメージ書き込み (3) 実行 & 結果(json) (4) 成否判定 集計 表示 (5) exit codeを返す (0)テストコードのビルド probe-rsとマイコン間の文字入出力や終了コードのやり取りにはsemihostingを用いる
probe-rs run(test mode)の機能 • probe-rs runにtest用オプションを与えるとtest modeで動作する • https://github.com/probe-rs/probe- rs/blob/c6dae5f3e0db1af0e159f6e6aff12df33d8ab447/probe-rs-
tools/src/bin/probe-rs/cmd/run.rs#L285 • test modeでは以下の機能を提供する • テストの実行・制御 • テスト結果の収集・表示 2025/05/11 28
テストモードのoverview 1. マイコンへのプログラムの書き込み & 実行 2. マイコン側からテストの一覧を取得 1. semihosting経由で list
コマンドを実行してテスト関数一覧を取得 • SessionInterface::list_testsとlist_tests_impl関数が担当 3. テスト一覧に対し、以下を繰り返し行う(foreach) 1. マイコンをリセット • https://github.com/probe-rs/probe-rs/blob/71d09fd6807353c49fddd98f28966e0d7e94b67e/probe-rs- tools/src/bin/probe-rs/rpc/functions/test.rs#L229 2. semihosting経由で run <テスト名> を実行 • https://github.com/probe-rs/probe-rs/blob/71d09fd6807353c49fddd98f28966e0d7e94b67e/probe-rs- tools/src/bin/probe-rs/rpc/functions/test.rs#L260 3. 実行結果を取得し、成否を画面に表示 4. 実行結果をexit codeとして返す • 成功時は0, 失敗時はそれ以外 • cargo testはこの値を元に成否を判定 2025/05/11 29
ここで気になったこと • どうやってstd環境のtestのような表示を実現しているか • panicするテストはどうやって実現しているか 2025/05/11 30
テストの実行と画面表示の方法 • probe-rsはlibtest_mimicを用いて cargo test とほぼ同じ出力を提供し ている • 右図参照 •
probe-rsのやっていること • テストの処理をclosureにしてTrial型の リストを作成 • https://github.com/probe-rs/probe- rs/blob/c6dae5f3e0db1af0e159f6e6aff12 df33d8ab447/probe-rs- tools/src/bin/probe-rs/util/cli.rs#L591- L623 • libtest_mimic::runで全テストを実行・ 結果表示 • https://github.com/probe-rs/probe- rs/blob/c6dae5f3e0db1af0e159f6e6aff12 df33d8ab447/probe-rs- tools/src/bin/probe-rs/util/cli.rs#L540 2025/05/11 31
panicするテスト(should_panic)の実現方法 • 通常はマイコン側がpanicしたらprobe-rsはテスト失敗 判定をする • マイコン側はsemihosting経由でabortを返す • probe-rsは以下の機序でpanic時にテストが失敗したと判定 • handle_halt関数はabortを受け取ったらTestOutcome::Panicを返す
• https://github.com/probe-rs/probe- rs/blob/c6dae5f3e0db1af0e159f6e6aff12df33d8ab447/probe-rs- tools/src/bin/probe-rs/rpc/functions/test.rs#L395-L397 • outcomeが期待通り(outcome == expected_outcome(Pass))でないな ら失敗 • https://github.com/probe-rs/probe- rs/blob/c6dae5f3e0db1af0e159f6e6aff12df33d8ab447/probe-rs- tools/src/bin/probe-rs/rpc/functions/test.rs#L265-L268 • マイコン側の関数に#[should_panic]がついている場合は 成功にする • should_panicがtrueの場合はexpected_outcomeがPanicにな る • (outcome == expected_outcome) が true になるので成功とな る 2025/05/11 32 probe-rsのテスト成否判定部分 outcomeが期待通りの場合は成功
should_panicの情報はどうやってマイコン からprobe-rs側に渡るの? • マイコン側のlistコマンド実行結果経由でprobe-rsに渡る • 出力例 • INFO tests available:
[Test { name: "tests::test_add", function: 0xe5, should_panic: false, ignored: false, timeout: None }] 2025/05/11 33
probe-rs+embedded-testのテストの仕組み • マイコン側(embedded-test)は以下の2機能のみを提供する • テストの一覧表示 • 指定されたテストの実行 • probe-rs(とlibtest_mimic)がその他の部分をすべて担当する •
テスト一覧(と詳細情報)取得 • 各テストの実行 • 実行結果取得 • 画面表示(libtest_mimicの機能) • 終了コード出力 • マイコン・probe-rs間の通信にsemihostingを用いる 2025/05/11 34
今回のまとめ • cargo testは以下しか行っていない • テスト用バイナリのビルド • 上記バイナリの実行と終了コードのチェック • 各テストの実行制御や結果の表示・集計はtest
crate側で行っている • probe-rs + embedded-testは協力してstd環境のcargo test相当の 機能を提供している • embedded-test • マイコン側でテストリストの出力、指定されたテストの実行を行う • probe-rs • テストの実行・実行後のリセット・結果の集計等を行う 2025/05/11 35
おまけ 2025/05/11 36
probe-rs + embedded_testが使えない環境 でこの機能を実現するには? • x86_64環境でのOS開発ではprobe-rs+embedded_testが使えない • semihosting機能がないため • x86_64+qemuならexit
deviceを使って模擬できるかも • マイコン側の機能はcustom test flameworksで対応できるかも • probe-rs側の機能は新規開発が必要そう • コストとメリットを天秤にかけて要検討 • コスト • 様々な開発が必要 • メリット • should_panicテスト等ができる • テスト結果の表示がきれいになる 2025/05/11 37
probe-rs + embedded_testが使えない環境 でこの機能を実現するには? 2 • 実機のx86_64マシンで自作OS等をテストするには? • 以下の仕組みを用意して、制御ソフトを作ればできるかも? •
リセット機能 • IoTコンセントとか? • プログラムのロード機能 • uefiのhttp bootとか? • 入出力 • UARTとか? (UART使えるお手頃x86マシンって今もあるのだろうか?) 2025/05/11 38