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がしたい/KernelVM Kansai 11
Search
Toshifumi NISHINAGA
May 11, 2025
Programming
1
1k
マイコンでもRustのtestがしたい/KernelVM Kansai 11
embedded-test crateを使うとマイコンターゲット(no_std)向けのRustでもテストができるよという話です。
内部的な実装の話やボツ案は資料限定です。
Toshifumi NISHINAGA
May 11, 2025
Tweet
Share
More Decks by Toshifumi NISHINAGA
See All by Toshifumi NISHINAGA
BareMetalで遊ぶRaspberry Pi 5 PCIe編/KernelVM Tokyo17
tnishinaga
0
2.1k
probe-rsの紹介と最近の貢献紹介/CELF-02-03
tnishinaga
1
450
SecurityCamp2023基板作るコース講義資料/Security Camp 2023 Lecture Materials
tnishinaga
8
2.6k
RP2040のPIOを使う話/KernelVM Hokuriku 6
tnishinaga
3
1.5k
JTAGでArmプロセッサをデバッグする方法のつづき/KernelVM_Tokyo16
tnishinaga
0
480
CMSIS-DAPの概要と使い方/KernelVM Online5
tnishinaga
0
1.9k
JTAGでarmプロセッサをデバッグする話/KernelVM Online4
tnishinaga
4
3.4k
ARM入門/arm introduction
tnishinaga
14
12k
俺の仮想マシンルーターがこんなに遅いはずはない/ KernelVM online 1
tnishinaga
0
3k
Other Decks in Programming
See All in Programming
Cloudflare Workersで進めるリモートMCP活用
syumai
13
1.9k
Parallel::Pipesの紹介
skaji
2
270
CursorとDevinが仲間!?AI駆動で新規プロダクト開発に挑んだ3ヶ月を振り返る / A Story of New Product Development with Cursor and Devin
rkaga
5
1.8k
Investigating Multithreaded PostgreSQL
macdice
0
130
Feature Flag 自動お掃除のための TypeScript プログラム変換
azrsh
PRO
4
560
rbs-traceを使ってWEARで型生成を試してみた After RubyKaigi 2025〜ZOZO、ファインディ、ピクシブ〜 / tried rbs-trace on WEAR
oyamakei
0
470
Duke on CRaC with Jakarta EE
ivargrimstad
1
600
コンポーネントライブラリで実現する、アクセシビリティの正しい実装パターン
schktjm
1
600
try-catchを使わないエラーハンドリング!? PHPでResult型の考え方を取り入れてみよう
kajitack
3
160
Design Pressure
hynek
0
1.4k
JVM の仕組みを理解して PHP で実装してみよう
m3m0r7
PRO
1
240
DevDay2025-OracleDatabase-kernel-addressing-history
oracle4engineer
PRO
6
1.5k
Featured
See All Featured
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.5k
Statistics for Hackers
jakevdp
799
220k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
15
880
Designing for humans not robots
tammielis
253
25k
VelocityConf: Rendering Performance Case Studies
addyosmani
329
24k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
180
53k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
105
19k
4 Signs Your Business is Dying
shpigford
183
22k
GraphQLとの向き合い方2022年版
quramy
46
14k
Optimising Largest Contentful Paint
csswizardry
37
3.3k
Product Roadmaps are Hard
iamctodd
PRO
53
11k
Typedesign – Prime Four
hannesfritz
41
2.6k
Transcript
マイコンでも Rustのtestがしたい @tnishinaga(CVついなちゃん) 2025/05/11 KernelVM@Kansai 11 https://github.com/tnishinaga/20250511_kernelvm_kansai_sample https://speakerdeck.com/tnishinaga/maikondemorustnotestgasitai-at-kernelvm-kansai-11 2025/05/11 1
今日のまとめ • マイコン(no_std)でもRustのテストをしたいなら、embedded- testを使おう • embedded-test + probe-rs • https://crates.io/crates/embedded-test
2025/05/11 2
背景 • マイコンで動くJTAGデバッガを(ほそぼそと)作り続けている • 色々忙しいおとしごろ • 次回こそはなにか出したい • 作ったコードのテストをしたくなった •
今どきのテスト方法は何があるんだろう? 2025/05/11 3
今日の目的 • マイコン(no_std)環境でもRustのテストを実施する方法を紹介 • 特にCortex-M系を対象 2025/05/11 4
テストとは? • プログラムが想定した動作をしているか確かめること • テストを積み上げることで…… • テストした部分の動作を保証できる • 開発中のデグレを検知し、手戻りを防げる •
その他嬉しいこと多数 • テストを先に書いて開発するテスト駆動開発(TDD)などもある • 現代的なプログラミングはテストは必須かつhigh priority • 適度かつ適切なテストを作ればtotalの開発コストは減る • ……と個人的に思っている 2025/05/11 5
test 1 (with test library) test runner 現代的なテスト TEST_CASE(should_err) {
should_err(err()) } ... run result run result 人間 CI (1) 実行 (2) 各種テスト手順の実施 (3) 実施結果の集計 (4) 結果確認 test N (with test library) TEST_CASE(should_ok) { should_ok(ok()) } fn pass() { return Ok } fn err() { return Err; } テスト対象 コード ※ 結果の活用等は省略 2025/05/11 10
現代的なテスト • テストフレームワークを使ってテストの保守性・生産性をUP • テストをtest caseごとにわけて登録 • 全test caseをrunner/frameworkが実行・成否判定・集計 •
失敗時はどこで失敗したかを報告 • 人間は成功・失敗を見るだけにする • CIとの相性も良い • テスト結果活用の仕組み導入もセット • slackで通知したり、githubのPR blockerにしたり • 今回は省略 2025/05/11 11
現代的なテストに必要なもの(C/C++) • テストランナー • この資料では「複数のテストを実施して結果を集計するもの」 • 例: ctestなど • テストライブラリ
• プログラム内でテストを簡単に行うためのヘルパーを提供 • assert関数、testcase記述機能、複数testcase実施機能等、mock作成支援 • 例: • google test(https://github.com/google/googletest) • catch2(https://github.com/catchorg/Catch2) • Unity(https://github.com/ThrowTheSwitch/Unity) 2025/05/11 12
Rustのテスト機能がカバーする範囲 ↑この範囲だいたい全部 2025/05/11 13
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 14 コード: https://github.com/tnishinaga/20250511_kernelvm_kansai_sample/tree/main/no01_std_tests
cargo test実行の様子 2025/05/11 15
cargo test実行結果 unit test 部分 integration test 部分 結果発表 成功時に
0を返す 2025/05/11 16
rustのテストの実現方法 • 以下を行ってテスト用バイナリを生成し、実行している • cargoがrustcにtest用オプション(--test)を渡す(※) • rustcがテスト用マクロやハーネス等を展開しつつビルド • cfg(test)の有効化 •
マクロ展開 • test用のハーネスを生成 • cargo testはtestの実行と集計を担当している(= runner) • っぽい(読み切れていない) • 詳細は公式のドキュメントを確認 • https://github.com/rust- lang/rust/blob/7295b08a17d1107155acd4b552069e3705b0ab1f/src/doc/rustc- dev-guide/src/test-implementation.md ※: cargo build --tests --lib -vvv するとコンパイルオプションが取れる 2025/05/11 17
テスト用マクロ展開 実 行 ハーネスを生成 (cargo expand --lib --tests) マクロ展開部 生成されたtest
harness 2025/05/11 18
--testオプションが渡されたrustcの挙動 • --test optionの受取 • rustc_session::options::Options型に格納 • https://github.com/rust- lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_session/src/config.rs#L2669 •
https://github.com/rust- lang/rust/blob/651e9cf327358b28db7e37a2ae61727f4a2ef232/compiler/rustc_session/src/config.rs#L2746 • 以降、rustc_session::session::is_test_crate関数で確認 • cfg(test) の有効化 • https://github.com/rust-lang/rust/blob/master/compiler/rustc_session/src/config/cfg.rs#L295-L298 • test harnessなどの生成 • harness: テスト専用main関数 • Cargo.tomlのharnessオプションで消せもする • 参考 • https://github.com/rust-lang/rust/blob/master/compiler/rustc_builtin_macros/src/test_harness.rs#L66-L90 • https://doc.rust-lang.org/test/fn.test_main_static.html • https://rust-exercises.com/advanced-testing/09_test_harness/00_intro#test-harness • proc macroの展開 • #[test] の展開 • (たぶん) ふつーのマクロ展開と同じ ※cargo build --tests --lib -vvv するとコンパイルオプションがわかる 2025/05/11 19
rustのテストの実現方法 • 以下を行ってテスト用バイナリを生成し、実行している • cargoがrustcにtest用オプション(--test)を渡す(※) • rustcがテスト用マクロやハーネス等を展開しつつビルド • cfg(test)の有効化 •
マクロ展開 • test用のハーネスを生成 • cargo testはtestの実行と集計を担当している(=runner) • っぽい(読み切れていない) • 詳細は公式のドキュメントを確認 • https://github.com/rust- lang/rust/blob/7295b08a17d1107155acd4b552069e3705b0ab1f/src/doc/rustc- dev-guide/src/test-implementation.md ※: cargo build --tests --lib -vvv するとコンパイルオプションが取れる 2025/05/11 20
これまでのまとめ • 現代的なテストの仕組みには以下が必要 • テストランナー • テストライブラリ・フレームワーク • Rustはテストランナー・ライブラリを持っている •
テストランナー • cargo test • ライブラリ・フレームワーク • rustcの生成するharness • libtest等 • Rustのテストはコンパイラが実行バイナリを生成している • マクロ展開 • ハーネス追加 2025/05/11 21
no_std(マイコン) Rustのテスト 2025/05/11 22
cargo testはtargetがマイコンでも動くの? • stdライブラリのないtargetではビル ドができない • stdがないので... • libtestがリンクできない •
test harnessが動かない • Vecとか使ってる • panic handler等がない • etc... 2025/05/11 23
マイコンでtestを行うにはどうすれば? • テストの依存対象や抽象度を整理し、 それぞれに適切な設定をすればテストできる 2025/05/11 24
• テスト方法を大雑把に以下の4つに分類 • host環境(64bit std)テスト • テスト対象がアーキテクチャ・ポインタ幅・ペ リフェラル等に依存しない • 64bit
std環境(host環境)でテスト可 • 32bit std環境テスト • 対象がポインタ幅にのみ依存する場合 • 32bit std環境(arm32 linux, i386 linux等)でテスト可 • 実機でもqemu上でも可 • qemu test • ポインタ幅・アーキテクチャに依存 • アーキテクチャを合わせたqemu上でテスト可 • 実機テスト • 実機(アーキテクチャ・ポインタ幅・ペリフェラ ル等)に依存 • 実機上でしかテストできない no_std testの整理 2025/05/11 25 自由度 高 自由度 低 実行時間 短い 忠実性 低 優先度 低 実行時間 長 忠実性 高 優先度 高 実機 host (std) 32bit std qemu
実機テスト • testが実行できない原因を取り除いて実機でテストする • 実機 + debugger + test用の何らかの仕組みを用いる •
pros • 忠実性が高い • 「実機で動くこと」が最優先事項よ! • コードに手を加える部分が少ない • featureでの切り替え等がいらない • cons • 実行時間が(比較的)長い • Raspberry Pi Picoで実行すると書き込みだけで約2秒程度かかる • 故障リスクがある • FLASHの書き換え上限・謎のcrashなどなど • CI上での実施に難あり • ハードウェアリソースが乏しい • 難しいテスト・大量のテストは実施できない 2025/05/11 26 DebuggerとRasPi Pico
実機テスト用の仕組み・ライブラリ • custom test frmeworks を使う • https://os.phil-opp.com/testing/ • https://doc.rust-lang.org/beta/unstable-book/language-features/custom-
test-frameworks.html • embedded-test + probe-rs を使う ←オススメ • https://crates.io/crates/embedded-test 2025/05/11 27
custom test frameworksを使う方法 • 通常rustcが用意する test harnessを全部自前で用意する方法 • custom test
frameworks(unstable feature)を使うとharnessを自作できる • https://doc.rust-lang.org/beta/unstable-book/language-features/custom-test- frameworks.html • 作り込み次第ではあるが、動作確認+α程度の仕組みはできる • やり方は「Writing an OS in Rust」に詳しく書かれている • https://os.phil-opp.com/testing/ • hikaliumさんのOS自作本にも書かれているらしい(まだ読んでない) • [作って学ぶ]OSのしくみⅠ, hikalium著, 技術評論社 2025/05/11 28 https://github.com/tnishinaga/20250511_kernelvm_kansai_sample/tree/main/no03_nostd_custom_test_frameworks
custom test frameworksを用いたテストの様子 2025/05/11 29
custom test frameworksを用いたテストの様子 2025/05/11 30
custom test frameworksを使ったテスト方法 1. lib.rsに以下を追記 1. #![no_main] 2. #![feature(custom_test_frameworks)] 1.
custom_test_frameworksを有効化 3. #![test_runner(test_runner)] 1. test runnerとしてtest_runner関数を指定 4. #![reexport_test_harness_main = "test_main"] 1. harnessとして追加する関数名をmainか らtest_mainに変更 2. test_runner関数を追記 1. 関数ポインタを受け取って実行する 2. 右図参照 2025/05/11 31 test runner
custom test frameworksを使ったテスト方法 1. mod testsにtest_caseを登 録 1. #[test] を
#[test_case]に置 き換えるだけ 2. mod testsにentry関数を追 加 1. 通常のマイコンエントリ ポイントからtest_mainを 呼ぶ 1. 必要なら先にsetupをする 2. test_mainがtest_runnerを呼 び、テストが実行される 2025/05/11 32
custom test frameworksのpros/cons • pros • primitiveな方法なので潰しが効く • アーキテクチャ依存等も特になさそう •
cons • 実装コストが高め • 全部自分で作る必要があってだるい • できないことも多い • テストの集計ができない • どのテストが成功・失敗したかわからん • should_panic等のテストもできない • panicしたらそこで終了 • nightlyが必要(=stableが使えない) • 業務利用はきびしそう 2025/05/11 33 わざとpanicさせた場合の出力 panic_probeでログ出力すればどこで落ち たかぐらいはわかる
embedded-test + probe-rsを使う方法 • macroを1行足すだけで、cargo testが マイコンでも実行できるようになる crate • 必要なもの・こと
• マイコンのdebugger • probe-rsのサポート • semihostingに対応していること 2025/05/11 34 https://github.com/tnishinaga/20250511_kernelvm_kansai_sample/tree/main/no04_nostd_embedded_test embedded-test使用時のlib.rs
embedded-testを用いたテストの様子 2025/05/11 35
embedded-testを用いたテストの様子 2025/05/11 36
embedded-testの仕組み • crateとprobe-rsを使って実現 • embedded-test • マイコン上でテストを実施し、cargo test用の出力を出す • cargo
testが解釈できるjsonを出してるっぽい(詳細未確認) • probe-rs • マイコンにテスト用firmwareをロードする • マイコン・cargo test間の通信を仲介する 2025/05/11 37 マイコン probe-rs Cargo test semihosting stdio
embedded-testの使い方 • 依存crateをCargo.tomlに足す • [dev-dependencies] embedded-test = { version =
"0.6.0" } • harness生成を消す • [lib] harness = false • .cargo/config.tomlに追記 • rustflagsにemmbedded-test.xを足す • "-Clink-arg=-Tembedded-test.x", • runnerにprobe-rsを指定する • runner = "probe-rs run --chip RP2040 --protocol swd" • lib.rsの #[cfg(test)]の下に1行追加 • #[embedded_test::tests()] 2025/05/11 38 embedded-test利用時のlib.rs
embedded-testのpros/cons • pros (custom test frameworksに比べて) • 手順が少なくて簡単 • nightly不要
• should_panicやタイムアウト等も対応可 • cons • semihostingに依存 • arm, riscv, x86_64(qemu)では動きそう。その他は無理かも。 • 追記: x86はsemihosting crateが対応していなかったので削除 • debuggerの繋げない環境では動かなさそう • シリアルで書き込むターゲット(debugger less pico)でも動いたら嬉しいが…… • ライブラリに手を入れればsemihosting依存は切れるかも? • 次ページ 2025/05/11 39
embedded-testのsemihosting依存を切る案 • 原理的にはcargo testとrunnerから起動したプログラムで双方向通信 ができれば動くはず • 案1: シリアル経由で動かす • 以下のrunnerプログラムを作る
• シリアル経由でfirmwareをマイコンに書き込む • シリアル経由でマイコンと通信し、cargo testとの双方向通信を仲介する • library側に手をいれる • semihosting使ってる部分をtraitにして、semihostingをoptionalにする 2025/05/11 40 マイコン Runner Cargo test serial stdio
• テスト方法を大雑把に以下の4つに分類 • host環境(64bit std)テスト • テスト対象がアーキテクチャ・ポインタ幅・ペ リフェラル等に依存しない • 64bit
std環境(host環境)でテスト可 • 32bit std環境テスト • 対象がポインタ幅にのみ依存する場合 • 32bit std環境(arm32 linux, i386 linux等)でテスト可 • 実機でもqemu上でも可 • qemu test • ポインタ幅・アーキテクチャに依存 • アーキテクチャを合わせたqemu上でテスト可 • 実機テスト • 実機(アーキテクチャ・ポインタ幅・ペリフェラ ル等)に依存 • 実機上でしかテストできない no_std testの整理 2025/05/11 41 自由度 高 自由度 低 実行時間 短い 忠実性 低 優先度 低 実行時間 長 忠実性 高 優先度 高 実機 host (std) 32bit std qemu • テスト方法を大雑把に以下の4つに分類 • host環境(64bit std)テスト • テスト対象がアーキテクチャ・ポインタ幅・ペ リフェラル等に依存しない • 64bit std環境(host環境)でテスト可 • 32bit std環境テスト • 対象がポインタ幅にのみ依存する場合 • 32bit std環境(arm32 linux, i386 linux等)でテスト可 • 実機でもqemu上でも可 • qemu test • ポインタ幅・アーキテクチャに依存 • アーキテクチャを合わせたqemu上でテスト可 • 実機テスト • 実機(アーキテクチャ・ポインタ幅・ペリフェラ ル等)に依存 • 実機上でしかテストできない
qemu + embedded-test • ターゲットとなるべく同じqemuマシン上でテストする • 今回はPicoとおなじcortex-m0のmicrobitのmachineを対象にテスト • pros •
(実機よりは)高速 • 故障等の可能性もない • 命令セット・アーキテクチャ依存までならテスト可 • cons • コードに手をいれる必要は少しある • qemu featureを作って実機依存を切り離す必要あり • 実機に比べると忠実性が下がる • メモリ・FLASHの制約は実機と変わらない • Cortex-M系はvirt machineが使えないため • (unit test専用のマイコン向けvirt machineほしい) 2025/05/11 42
qemu + embededd-testのやり方 • Cargo.tomlを修正 • rp_picoとqemu featureを追加 • qemu
feature有効時はmicrobit crate等を読み込む • libのharnessをfalseにする • [lib] harness = false • build.rsを修正してlinker scriptを切り替え • memory.xをout以下に生成 • .cargo/config.tomlを分離して、適切なファイルをcargoのオプション で指定 • runnerをpicoとqemuで切り替え • qemu用のrunner scriptを追加 • semihostingを有効化して起動 2025/05/11 43 細かい部分は以下を参照 https://github.com/tnishinaga/20250511_kernelvm_kansai_sample/tree/main/no05_nostd_embedded_test_qemu
qemu + embededd-testの実行結果 2025/05/11 44 ↑テスト成功してるらしい
qemu+embededd-testの課題 • 複数のテストを実行できない • defmt-printが入力を受け付けないため • defmt-print: defmt encodeされたテキストをdecodeするプログラム •
入力を受け付けるdefmt-print的な物があれば解決 • コスパは良くないかも • ライブラリの切り替えから何から結構めんどい • 実機を使い潰せるなら実機のほうが楽そう • 実機と同じmachineがqemuにあるなら便利かも? 2025/05/11 45
今日のまとめ • マイコン(no_std)でもRustのテストをしたいなら、embedded- testを使おう • embedded-test + probe-rs • https://crates.io/crates/embedded-test
2025/05/11 46