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
0
480
マイコンでも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
2k
probe-rsの紹介と最近の貢献紹介/CELF-02-03
tnishinaga
1
440
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
460
CMSIS-DAPの概要と使い方/KernelVM Online5
tnishinaga
0
1.8k
JTAGでarmプロセッサをデバッグする話/KernelVM Online4
tnishinaga
4
3.3k
ARM入門/arm introduction
tnishinaga
14
12k
俺の仮想マシンルーターがこんなに遅いはずはない/ KernelVM online 1
tnishinaga
0
2.9k
Other Decks in Programming
See All in Programming
The Implementations of Advanced LR Parser Algorithm
junk0612
2
1.4k
一緒に働きたくなるプログラマの思想 #QiitaConference
mu_zaru
80
20k
「理解」を重視したAI活用開発
fast_doctor
0
280
今話題のMCPサーバーをFastAPIでサッと作ってみた
yuukis
0
110
Beyond_the_Prompt__Evaluating__Testing__and_Securing_LLM_Applications.pdf
meteatamel
0
110
インプロセスQAにおいて大事にしていること / In-process QA Meetup
medley
0
140
音声プラットフォームのアーキテクチャ変遷から学ぶ、クラウドネイティブなバッチ処理 (20250422_CNDS2025_Batch_Architecture)
thousanda
0
400
監視 やばい
syossan27
12
10k
2ヶ月で生産性2倍、お買い物アプリ「カウシェ」4チーム同時改善の取り組み
ike002jp
1
110
Boost Your Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
800
サービスレベルを管理してアジャイルを加速しよう!! / slm-accelerate-agility
tomoyakitaura
1
200
スモールスタートで始めるためのLambda×モノリス(Lambdalith)
akihisaikeda
2
370
Featured
See All Featured
The Invisible Side of Design
smashingmag
299
50k
Building Flexible Design Systems
yeseniaperezcruz
329
39k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
410
What's in a price? How to price your products and services
michaelherold
245
12k
Facilitating Awesome Meetings
lara
54
6.3k
Optimizing for Happiness
mojombo
378
70k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.2k
How STYLIGHT went responsive
nonsquared
100
5.5k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.3k
Fireside Chat
paigeccino
37
3.4k
Gamification - CAS2011
davidbonilla
81
5.3k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
137
33k
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)では動きそう。その他は無理かも。 • 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