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
internal/testlog で遊ぼう
Search
rokuosan
August 24, 2026
Technology
94
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
internal/testlog で遊ぼう
layerx.go #5 LT
https://layerx.connpass.com/event/402006/
rokuosan
August 24, 2026
More Decks by rokuosan
See All by rokuosan
発見!Ruby 9
rokuosan
0
43
CloudflareのBetaサービスでローカルなLLMを安全に公開する
rokuosan
0
260
Workers を定期実行する方法は一つじゃない
rokuosan
0
540
OpenNext + Hono on Cloudflare でイマドキWeb開発スタックを実現する
rokuosan
0
640
Kotlin で始める MCP 入門
rokuosan
0
340
【学内向け】主観でやるオレオレ RubyKaigi 2025 Recap
rokuosan
0
42
Kotlin で Discord の絵文字コミュニケーションを盛り上げたい!
rokuosan
0
21
ずっと昔に Star をつけたはずの思い出せない GitHub リポジトリを見つけたい!
rokuosan
0
370
KC3 2024 Dockerで始める自宅サーバー入門
rokuosan
0
740
Other Decks in Technology
See All in Technology
RapidCopy2 Matrix I/Oエンジンによるファイルコピーソフトウェアの設計と実装
kengosawa2
1
380
Go 1.27 の標準パッケージに uuid が入った!のでいろいろ喋る / go_127_std_go_uuid
convto
3
700
AIのためのEthernet技術動向 (SerDes)
markunet
1
310
ハッカソンで入賞した話 @ Findy LT
asari194617
0
1.5k
Kiro Crew入門 - 常駐エージェントの仕組みと使いどころ / Intro to Kiro Crew
k_adachi_01
1
890
[potatotips #96] Give Your AI Agent the Flutter Playbook
korodroid
0
160
8bit CPU 2026
koba789
7
2.8k
AI開発に用いられるHPC技術について
gpuunite_official
0
270
わたしが知り合いゼロの勉強会に 行けるようになるまで
r5ni4
2
480
会社紹介資料 / Sansan Company Profile
sansan33
PRO
24
430k
【GCC2026】大規模言語モデルを活用した内製検索サービスの社内展開や業務活用
bandainamcostudios
PRO
0
520
Contract One Engineering Unit 紹介資料
sansan33
PRO
0
20k
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
470
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
660
ラッコキーワード サービス紹介資料
rakko
1
4.5M
[SF Ruby Conf 2025] Rails X
palkan
2
1.3k
Design in an AI World
tapps
1
290
Believing is Seeing
oripsolob
1
200
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Docker and Python
trallard
47
4.1k
30 Presentation Tips
portentint
PRO
1
380
Embracing the Ebb and Flow
colly
88
5.1k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.9k
Transcript
internal/testlog で遊ぼう 2026-08-24 layerx.go #5 @rokuosan_dev
whoami @rokuosan_dev rokuosan(ろくお) id:rokuokun 敬称は省略できます LayerX バクラク事業部 (2026-08 -) Platform
Engineering 部 Enabling チーム インターンです 京都在住 Kyoto.kt 主催 © LayerX Inc.
go test (cached)
緊急 キャッシュされる?されない?クイズ
Q1. 通常の Go test 例えばこんなテスト © LayerX Inc. 5
Q1. 通常の Go test go test を素直に実行する 当然テストが実行される © LayerX
Inc. 6
Q1. 通常の Go test Q1. 2回目は? © LayerX Inc. 7
Q1. 通常の Go test A2. 2回目は cached そうですね © LayerX
Inc. 8
Q2. 依存する環境変数やファイルを変更したテスト Q2. 環境変数やモジュール内のファイルを変更すると? © LayerX Inc. 9
Q2. 依存する環境変数やファイルを変更したテスト A2. 実行される © LayerX Inc. 10
Q3. モジュール外の変更 モジュール外のファイルを参照してみる © LayerX Inc. 11
Q3. モジュール外の変更 Q3. モジュール外のファイルを書き換えたら? © LayerX Inc. 12
Q3. モジュール外の変更 キャッシュされてテストは走らない ファイルを書き換えたのに走らない 困った © LayerX Inc. 13
Q3. モジュール外の変更 強制的に実行させる 無事失敗 © LayerX Inc. 14
cache の仕組み
キャッシュ利用の判定 testID で testlog.txt を取得 testID = テストバイナリの build ID
+ キャッシュ可能な引数 testlog の各行を今の状態でハッシュして inputsID を作る → 現在の値。未設定かどうかも込み open → mtime と size (中身は読まない) stat → Stat と Lstat の結果 GODEBUG はログに出てこなくても必ず混ぜる getenv subkey(testID, inputsID) © LayerX Inc. で前回の出力を取得できれば (cached) ※ subkey(parent, desc) は親キーと説明文字列を混ぜて派生キーを作る cmd/go 共通のヘルパ。stdout や coverprofile の保存にも使われている 16
キャッシュ判定の擬似コード © LayerX Inc. ref: cmd/go/internal/test/test.go tryCacheWithID / computeTestInputsID 17
testlog.txt テストが os 越しに触ったものが、そのまま1行ずつ載る モジュール root の外にあるファイルはログには載るが、ハッシュの対象にならない フォーマットについては後述 © LayerX
Inc. 18
internal/testlog
internal/testlog Package testlog provides a back-channel communication path between tests
and package os, so that cmd/go can see which environment variables and files a test consults. テストと os の間のバックチャネル cmd/go testing が「テストがどの env / file を見たか」を知るためにある が実装を挿し、 os がそれを呼ぶ 両者が共通で依存できる最下層に置かれている © LayerX Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/internal/testlog/log.go#L5-L7 20
Interface os © LayerX Inc. 側は testlog.Open(name) と呼ぶだけ logger が
nil なら何もしない ※ atomic.Pointer なのは、init で起きた goroutine と競合しても race detector に引っかからないようにするため 21
internal/testlog を自作する
Interface を用意する 同じメソッドを同じシグネチャで並べた interface を自前で定義する internal/testlog.Interface © LayerX Inc. は
internal なため... 23
フォーマットにそって実装する 1行目は # test log で固定 これが無いと malformed 扱いで丸ごと捨てられる あとは
op name を1行ずつ。末尾は必ず改行 op は open / stat / getenv / chdir の4つだけ © LayerX Inc. ref: cmd/go/internal/test/test.go testlogMagic 24
どうやって動かす? © LayerX Inc. 25
go:linkname による差し込み 本体の無い関数を宣言して、リンカに繋がせる 実体は internal/testlog.SetLogger 渡すのはさっきの自前 interface © LayerX Inc.
26
敗北... Go 1.23 以降のリンカの制限 定義側にマークが無いシンボルは引けない -ldflags=-checklinkname=0 © LayerX Inc. で黙らせる
27
checklinkname=0 でも敗北... SetLogger © LayerX Inc. は一度しか呼び出せない ref: https://github.com/golang/go/blob/go1.26.7/src/testing/internal/testdeps/deps.go#L117-L130 28
SetLogger を呼ぶ経路 © LayerX Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/testing/testing.go#L2600-L2680 29
SetLogger を呼ぶ経路 -test.testlogfile © LayerX Inc. は go test が毎回付けてくる
cmd/go がテストバイナリの os.Args を組み立てている *testlog はその値を flag.Parse が受け取ったもの ref: https://github.com/golang/go/blob/go1.26.7/src/testing/testing.go#L2600-L2680 30
SetLogger を呼ぶ経路 ここで一度目の SetLogger が呼ばれる さっきの panic はここと衝突 © LayerX
Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/testing/testing.go#L2600-L2680 31
SetLogger を呼ぶ経路 ここを false のままにしたい os.Args © LayerX Inc. から
-test.testlogfile を消せば、この経路ごと実行されない ref: https://github.com/golang/go/blob/go1.26.7/src/testing/testing.go#L2600-L2680 32
SetLogger を呼ぶ経路 init © LayerX Inc. で os.Args を書き換えると良さそう ref:
https://github.com/golang/go/blob/go1.26.7/src/testing/testing.go#L2600-L2680 33
os.Args の書き換えと Logger の置き換え を空文字にしてデフォルトの testlog を無効化 独自実装の testlog を差し込む
*testlog © LayerX Inc. 34
自作 testlog を仕込む
blank import による利用 で書いたのでこれで完了 ただしパッケージ単位... init() 差し替えたい全パッケージに1行ずつ書く必要がある © LayerX Inc.
36
overlay による差し込み -overlay © LayerX Inc. は存在しないパスを実ファイルに割り当てられる 37
差し込んでみる © LayerX Inc. 38
実行してみる © LayerX Inc. 39
ここからが本題
自作 testlog で遊んでみる © LayerX Inc. 41
GitHub Actions で (cached) 出ない問題 checkout のたびに全ファイルの mtime が更新される キャッシュを復元しても無駄
中身ではなく mtime で判定しているため golang/go にも issue がある golang/go #58571 © LayerX Inc. ref: golang/go#58571 「cmd/go: test cache hash inputs include ModTime, often causing cache misses on CI」 42
mtime はどこでハッシュに入るか(1/2) / mode / mtime / isdir をハッシュに書く #58571
でも言及されている size © LayerX Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/cmd/go/internal/test/test.go#L2138-L2140 43
mtime はどこでハッシュに入るか(2/2) これを実行すると以下の testlog が出る cmd/go はこの行を見て os.Stat し、実ファイルの mtime
をハッシュに混ぜる これによって checkout のたびに実ファイルの mtime は新品になる © LayerX Inc. 44
余談: なぜ中身ではなく mtime なのか 中身を丸ごとハッシュしない 巨大なファイルを毎回読むコストを避けるため 代わりに mtime + size
で「たぶん変わっていない」を推測する 正確だが遅い方法を捨てて、速いが不正確な方法を選んでいる © LayerX Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/cmd/go/internal/test/test.go#L2107-L2110 45
中身が変わったものだけ mtime が動いてほしい © LayerX Inc. 46
自作 testlog で見る先をすり替える testlog.txt は扱えるようになった 実ファイルの代わりに、中身が変わった時だけ mtime が動く代理ファイルを指すよう に書き換える test
実行前に中身が更新されていたら上書きする © LayerX Inc. 47
余談: なぜ open ではなく stat で書くか hashOpen では2秒以内に更新されたファイルの結果を保存しない仕様がある mtime の精度ではごく短い間隔の更新を区別できないことがあるため
© LayerX Inc. ref: https://github.com/golang/go/blob/go1.26.7/src/cmd/go/internal/test/test.go#L2112-L2118 48
こんな感じ テスト実行前に生成を忘れると破滅することには注意 古いハッシュを見続けるためずっと OK のまま © LayerX Inc. 49
まとめ (cached) は中身ではなくサイズや mtime で判断している internal/testlog を通じて、前回何を触ったかが記録されている internal package でも
linkname と overlay で挙動を変更できる GitHub Actions で別ジョブを跨ぐと go test は cached にならない checkout のたびに mtime が新品になるため 自作 testlog を使えば常に成功する go test を爆誕させることも可能 © LayerX Inc. 50