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
AI時代を見据えたコードカバレッジ計測ツールの開発
Search
Masaaki Goshima
February 23, 2026
Programming
55
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
AI時代を見据えたコードカバレッジ計測ツールの開発
Masaaki Goshima
February 23, 2026
More Decks by Masaaki Goshima
See All by Masaaki Goshima
Goにおけるインナーソースモデル構築への道のり
goccy
1
410
Go で WebAssembly を利用した 実用的なプラグインシステムの構築方法
goccy
8
4.1k
BigQueryエミュレータの作り方
goccy
1
5.6k
GoとJSON
goccy
1
220
エンジニアリングを支援するための様々なかたち
goccy
0
1.9k
最速のJSONライブラリを求めて
goccy
16
13k
PWAゲームを開発しネイティブアプリ化までした中での課題と対策
goccy
7
5k
Other Decks in Programming
See All in Programming
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
190
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.6k
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
150
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
160
さぁV100、メモリをお食べ・・・
nilpe
0
160
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
600
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
310
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
140
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1k
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
410
ふつうのFeature Flag実践入門
irof
8
4.2k
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.8k
Featured
See All Featured
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
260
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
620
Statistics for Hackers
jakevdp
799
230k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
300
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
The Curse of the Amulet
leimatthew05
2
13k
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Raft: Consensus for Rubyists
vanstee
141
7.6k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.1k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Transcript
AI時代を見据えた コードカバレッジ計測ツールの開発 Go Conference mini 2026 in Sendai goccy
Go Conference mini 2026 in Sendai 自己紹介 • goccy (
ごっしー ) ◦ Twitter(新X): @goccy54 / GitHub: @goccy • 仙台市泉区出身 • Go リポジトリの総獲得 Star 数:10K+ ◦ 個人OSS: go-json / go-yaml / bigquery-emulator etc. ◦ 企業OSS: grpc-federation etc. • Go Conference mini 2022 in Sendai ◦ BigQueryエミュレータの作り方
Go Conference mini 2026 in Sendai Agenda • Motivation ◦
なぜ新しいコードカバレッジツールが必要だと思ったのか • Tobari ◦ AI時代を見据えたコードカバレッジツールの紹介 • How it Works ◦ どうやって作ったのか
Go Conference mini 2026 in Sendai Motivation
Go Conference mini 2026 in Sendai AI時代の開発と品質保証 AI (Coding Agentなど)
により開発速度が上がっている一方、 その品質保証を人間が担う部分で律速になる課題がある • 大量のPRのレビュー / 人が行うQAプロセス 自動テストで品質保証する重要性が高まる 品質保証するために必要十分なテストとは? 課題 テストカバレッジが 100% に近いテストと定義 100%に近いコードカバレッジを 提供するテストを 効率よく生成したい
Go Conference mini 2026 in Sendai Go のカバレッジツールが抱える課題 Granularity (
計測粒度 ) Isolation ( 計測対象の隔離 )
Go Conference mini 2026 in Sendai Granularity ( 計測粒度 )
の課題 • テスト全体で通った場所はわかるが、どのテストで通ったかはわからない TestA ( X% coverage ) TestB ( Y% coverage ) TestA + TestB = 100% coverage X is 0% or 30 % or 50% or 100% ….?
Go Conference mini 2026 in Sendai Isolation ( 計測対象の隔離 )
の課題 • runtime/coverage を使った go test 以外の計測を考える ◦ e.g.) HTTP / gRPC Server に対するリクエストによる計測 • テストと関係のないカバレッジを取るリスクがある 1 5 2 6 Coverage Request A Coverage Request B Normal Request Goroutines 3 4 gRPC or HTTP Server 数字: 処理順 6 を処理したあとに カバレッジを取得すると、 1~6 すべてが結果に含まれる 本当は 1と6の結果だけ欲しい
Go Conference mini 2026 in Sendai Coding Agent がテストを生成する際の影響 •
テストごとのカバー範囲が正確にわからないため、全体で 100% に近づける ようにテストケースを作成する • 同じカバー範囲のテストが大量に作られる可能性がある ◦ CI の時間・費用、レビューやメンテナンスのコストが増える CI Time Review Cost Maintenance Cost
Go Conference mini 2026 in Sendai あるテストによって、どの部分がカバーされたのかを正確に把握できれば、 Coding Agent が意味的に重複がないテストを生成する精度が上がるのでは?
Go のコードカバレッジ計測ツールを自作し、検証することに 仮説 検証
Go Conference mini 2026 in Sendai Tobari - 帷 -
Go Conference mini 2026 in Sendai Tobari - 帷 -
• github.com/goccy/tobari • テストごとに通過した場所を正確に記録することができる • go test で使ったり、 runtime/coverage と同じように Server でも使える ◦ Goのカバレッジ計測ツールの代わりに使える • Install: go install github.com/goccy/tobari/cmd/tobari@latest
Go Conference mini 2026 in Sendai How to use •
with go test1 ◦ GOFLAGS=$(tobari flags) go test ./… ▪ tobari flags: -cover -toolexec=tobari を出力 ▪ 実行すると tobari/tobari.(json|toon) が作られる ▪ テスト対象を一切変更する必要がない • NOT go test1 ◦ gRPC Server InterceptorやHTTP adapterの中で、カバレッジリクエストのと きに tobari.CoverWithName(name, entryFunc) を呼ぶ ▪ name : カバレッジを分けたい単位で設定する識別子 ▪ entryFunc : カバレッジを取得し始めるエントリ関数
Go Conference mini 2026 in Sendai 計測結果の利用方法 • HTML 表示機能を利用してカバレッジ範囲を目視で改善
• Tobari の Agent Skills を使ったテストコードの自動改善
Go Conference mini 2026 in Sendai How it Works
Go Conference mini 2026 in Sendai (前提) Go のカバレッジ計測の仕組み 1.
go (test|build|run) のときに -cover option をつける 2. Goコンパイラがカバレッジ対象のパッケージを特定し、 そのパッケージのファイルを引数に go tool cover を呼び出す 3. go tool cover の中で受け取ったソースコードを AST に変換 4. AST からカバレッジ計測ポイント ( 関数の先頭や Ifによる分岐など ) を見つけ、 カウンタを挿入したコードを生成 5. 生成したファイルの場所を go tool cover の -outfilelist option で指定したファイルに書く 6. go tool compile が生成したコードを対象に走ってカバレッジ付きのバイナリになる go test -cover ./ Go compiler go tool cover Instrumented Code go tool compile Binary with coverage Identify cover targets AST transform Insert counters
Go Conference mini 2026 in Sendai Key Idea: Coverage with
GoroutineID 1. go tool cover の実行を自前のツールに差し替える 2. ASTを編集して計測ポイントを追加する際に、親と自分の GoroutineID も一緒に保存する 3. tobari.CoverWithName(name, entryFunc) API を提供 ◦ アプリ側で、識別子とカバレッジを取りたい関数を指定させる 4. tobari.CoverWithName の entryFunc 呼び出し時の GoroutineID を記録すれば、 GoroutineID ベースでどこを通ったかがわかる tobari.CoverWithName(name, entryFunc) entryFunc ( GID 10 ) foo ( GID 20, PGID: 10 ) bar ( GID 30, PGID: 20 ) GID: Goroutine ID PGID: Parent Goroutine ID
Go Conference mini 2026 in Sendai How to replace go
tool cover ? • Toolexec を利用する ◦ go (build|run|test) などで利用できる option ( -toolexec ) ◦ go tool compile などの tool 呼び出しをフックできる ▪ go build -toolexec=foo ./ • foo compile … or foo link … のような引数で foo が呼ばれる • go tool cover をフックすることもできる ◦ foo cover …
Go Conference mini 2026 in Sendai How to get (
parent ) GoroutineID ? • runtime.getg() で現在の Goroutine 情報がわかる ◦ runtime.getg().goid : GoroutineID ◦ runtime.getg().parentGoid : Parent Goroutine ID • Toolexec を利用して go tool compile の runtime package の compile をフックして以下の内容を加えると動的に公開 APIが作れる package runtime func GID() uint64 { return getg().goid } func PGID() uint64 { return getg().parentGoid }
Go Conference mini 2026 in Sendai Tips: go tool compile
をフックする上での注意
Go Conference mini 2026 in Sendai ビルドキャッシュの分離 • コンパイル時にファイルを置き換える場合、ビルドキャッシュが効くと go
tool compile がそもそも呼ばれないので、キャッシュ管理が必須 • Go コンパイラは各 go tool を呼び出す前に、必ず go tool compile -V=full の ように -V=full 付きのリクエストを行う ◦ -V=full の標準出力の結果を使って Build Cache ID が決まる仕様 ◦ 出力結果を変えるとキャッシュが別になるのでフルビルド ◦ @sivchari さんに教えてもらった • Tobari では、Tobari binary の hash値、置き換え処理で利用するファイルの hash値 などを組み合わせて Build Cache ID を作っている ◦ tobari: <replace-file-hash> exe:<binary-hash>
Go Conference mini 2026 in Sendai 依存パッケージの管理 • 置き換えたファイルが新しい依存を持っている場合、そのパッケージをビルドした上で、 go
tool compile がコンパイルできるように、ビルドしたパッケージへのパスを 教える必要がある • go tool compile には -importcfg import.cfg という option があり、 import.cfg が依存パッ ケージのリスト管理している ◦ packagefile <package-name>=<package-path> の羅列 ◦ -x option をつけてビルドすると簡単に中身が見れる e.g.) go build -x ./ • import.cfg にビルド結果を追記すれば良い • ビルドは go list -deps -export -json <package> を使う ◦ <package> をビルドし、依存 package の path も含めて JSON 形式で取得できる
Go Conference mini 2026 in Sendai 計測コードの挿入時の課題 • go tool
cover に代わって自前のコードを挿入する際、次のようなコードを追加したい ◦ tobari.Trace(runtime.PGID(), runtime.GID(), startLine, startCol, …) • もともとカバレッジ対象の package が runtime や tobari package に 依存していない場合、 import.cfg への追記が必要になり処理が複雑になる package foo import ( “runtime” “github.com/goccy/tobari” ) func Foo() { tobari.Trace(runtime.PGID(), runtime.GID(), 6, 11, …) }
Go Conference mini 2026 in Sendai linkname を利用した依存解決 • linkname
directive を利用することで runtime や github.com/goccy/tobari package を import せずにコンパイルできる • ただし、go tool link 時に指定する import.cfg には必ず runtime や github.com/goccy/tobari への依存が必要なので注意 import _ “unsafe” //go:linkname runtime_GID runtime.GID func runtime_GID() uint64 //go:linkname tobari_Trace github.com/goccy/tobari/internal/tobari.Trace func tobari_Trace(uint64, uint64, int, int, int, int, int, int) func Foo() { tobari_Trace(runtime_PGID(), runtime_GID(), 6, 11, …) }
Go Conference mini 2026 in Sendai 静的解析による到達範囲推定 • go tool
cover で渡されたコードを静的解析し、関数間の依存関係を記録する • Tobari のカバレッジ測定エントリポイントから呼ばれた関数を記録 ◦ tobari.CoverWithName(name, func() { foo() }) • 実際に呼ばれた関数 ( foo )が依存する関数全てを静的解析の結果から導き、 それを通過すべき範囲とする • foo から絶対に呼ばれない関数は、通過すべき範囲に含まれない
Go Conference mini 2026 in Sendai まとめ • Go標準の仕組みの代わりに使えるコードカバレッジ計測ツール、 Tobari
を開発 • GoroutineID を使って、テストごとのカバレッジデータを正確に測定 • Agent Skills などを通して Coding Agent に有益な情報を提供することが目的 • GOFLAGS=$(tobari flags) go test ./ でなぜ測定できるのか ◦ Toolexec の活用 ▪ go tool cover をフックして GoroutineID 付きで計測 ▪ go tool compile をフックしてコンパイル対象を差し替え ◦ linkname を活用しながらカバレッジ計測コードを挿入 ◦ 静的解析を使ってカバー範囲を推定
Go Conference mini 2026 in Sendai
Go Conference mini 2026 in Sendai Appendix
Go Conference mini 2026 in Sendai Coverage Metadata and Countdata
• Go のカバレッジデータは内部的に Metadata と Countdata に分かれている • Metadata ◦ go tool cover を実行した際に作成 ◦ package の名前やファイルパスなど静的に決まる情報を保持 • Countdata ◦ ソースコードの場所に紐づくカウントを保持 • coverprofile 形式で出力する場合は Metadata と Countdata を合成する • go test では、裏で作る testmain.go で合成し、結果を出力
Go Conference mini 2026 in Sendai Overlay • 標準ライブラリのコンパイル対象を変更できる ◦
replace: $GOROOT/src/runtime/stubs.go => /tmp/stubs.go ◦ add: $GOROOT/src/runtime/new.go => /tmp/new.go • Goコンパイラ側が置き換えるべきファイルを解析し、 ビルドキャッシュを使うか判断したり、 package の依存解決も行う • 標準ライブラリにもともと書いてあったかのように振る舞う • $GOMODCACHE 以下のファイルには適用できない ◦ toolchain directive を利用してインストールされる Go は $GOMODCACHE 以下に配 置されるため、置き換えられない ◦ GOTOOLCHAIN=local が必須
Go Conference mini 2026 in Sendai Toolexec Tips: Importcfg •
go tool compile と go tool link で指定される • go tool compile ◦ Signature の Check に利用される ◦ I/F が一緒なら、中身はなんでも良い • go tool link ◦ 依存している package すべての symbol が同じものである必要がある ◦ 例えば異なる Overlay ファイルからビルドされた同じ名前の package が依 存にあると fingerprint mismatch error が発生するので注意
Go Conference mini 2026 in Sendai Toolexec Tips: BuildID •
Toolexec では go tool (compile|cover|vet|link) が別プロセスで起動する が、 go build ごとにユニークな共通のデータにアクセスしたいことがある • go tool の呼び出しは go build の子プロセスになるため、 os.Getppid() を 使うことで go build プロセスの PID を取得できる • go build ごとにユニークな ID として利用できるため、これを使って 一時ディレクトリを作ってリソースを共有することができる go build ( PID: 10 ) go tool compile … (PID: 20, PPID: 10 ) go tool cover … ( PID: 30, PPID: 10 ) go tool link … ( PID: 40: PPID: 10 )
Go Conference mini 2026 in Sendai How to build github.com/goccy/tobari
package ? • github.com/goccy/tobari の package を link 時にビルドするため、 filepath.Join(os.TempDir(), “tobari”, os.Getppid(), “app”) 配下に main.go と go.mod を配置 ◦ main.go: import _ “github.com/goccy/tobari” を追加 ◦ go.mod: require github.com/goccy/tobari <version> を追加 ▪ <version> は Tobari 自身が知っているのがポイント • “app” の下で go list -deps -export -json -toolexec=tobari github.com/goccy/tobari を実行すると build した package への path が手に入る
Go Conference mini 2026 in Sendai テスト実行時のカバレッジ出力方法 GOFLAGS=$(tobari flags) go
test ./… でカバレッジをテスト単位で出力するため、 testing package の関数を置き換えている GS=$(tobari flags) go test ./…FLAGS=$(tobari package testing import _ "unsafe" //go:linkname tobari_cover github.com/goccy/tobari/internal/tobari.Cover func tobari_cover(name, entryID string) func (t *T) Run(name string, f func(*T)) bool { // オリジナルの (*testing.T).Run を呼び出す return t.orgRun(name, func(t *T) { // Test名とGoroutineIDを紐づける tobari_cover(t.Name(), t.Name()) f(t) }) } package testdeps // カバレッジ出力のために呼び出される関数 func coverTearDown( coverprofile string, gocoverdir string, ) (string, error) { // メタデータとカウントデータを設定 // tobari/tobari.json に出力 } testing/internal/testdeps.coverTearDown testing.(*T).Run
Go Conference mini 2026 in Sendai 静的解析時の依存グラフの調整 • 探索範囲を最適化 ◦
runtime, net/http, google.golang.org/grpc に到達したら打ち切る • runtime: GC 関連の API が全関数への依存を持つため無視 • net/http or google.golang.org/grpc: 全ハンドラへの参照を持つため無視
Go Conference mini 2026 in Sendai 埋め込み Option ( –embed-code
) の提供 • go test を利用しないパターンで coverage を取得した場合、 ビルドに利用したソースコードが手元にないことが多い ◦ e.g.) Container Image を作るタイミングでソースコードが消えている • Tobari では、 toolexec の option として —embed-code option をサポート ◦ go tool cover の処理を行う際に引数で渡されたソースコードを埋め込む ◦ tobari.ReadCoverArchivedFile() API を呼び出すと埋め込んだソースコー ドを tar.gz 形式で取得できる • Tobari の HTML 表示コマンドにはソースコードを渡すために tar.gz を渡す option や、ファイルを埋め込んだバイナリ自体を指定する option があり、 それを利用すると簡単に HTML 化ができる
Go Conference mini 2026 in Sendai