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
時の止め方を考える
Search
tenntenn - Takuya Ueda
PRO
October 23, 2024
Technology
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
時の止め方を考える
Go Connect#3で発表した資料です。
https://gotalk.connpass.com/event/331992/
tenntenn - Takuya Ueda
PRO
October 23, 2024
More Decks by tenntenn - Takuya Ueda
See All by tenntenn - Takuya Ueda
(続)生成AIによる 静的解析ツールの自動生成
tenntenn
PRO
0
81
まずはイテレータ(range over func)の仕様を学ぼう
tenntenn
PRO
0
480
モチベーションの維持と世代交代から考える持続可能なコミュニティ運営
tenntenn
PRO
0
180
fmfm🤔 fmtパッケージ - tenntenn.go#4
tenntenn
PRO
0
230
あなたの知らない go listコマンド - tenntenn.go#4
tenntenn
PRO
1
1.5k
ライブペアプログラミング その2 - tenntenn.go#3
tenntenn
PRO
0
97
sync.Onceを完全に理解する - tenntenn.go#3
tenntenn
PRO
0
580
Go 1.18で追加されるstrings/bytes.Cutと(*sync.Mutex).TryLockについて - Go1.18リリースパーティ
tenntenn
PRO
0
690
io/fsパッケージを読む - tenntenn.go#2
tenntenn
PRO
0
280
Other Decks in Technology
See All in Technology
AIを「創る」と「使う」の循環 — HRテックが実践するリアルなAI組織実装
taketo957
0
1.7k
マーケットプレイス版Oracle WebCenter Content For OCI
oracle4engineer
PRO
5
1.8k
探して_入れて_作って_使う_Agent_Skills___LT.pdf
peintangos
2
180
個人の発見を、組織の知恵に 〜生成AI活用を"探索"から"組織の仕組み"へ〜
kintotechdev
3
1.1k
Databricks における 生成AIガバナンスの実践
taka_aki
1
350
タクシーアプリ『GO』の実践的データ活用
mot_techtalk
3
170
チームで実践する AI-DLC 思考の軌跡を残すチェックポイント設計
belongadmin
0
3k
もりもり新機能を一挙紹介! AgentCoreに入門して、AWS上にAIエージェントを構築しよう
minorun365
PRO
6
850
PHP と TypeScript の型システム比較:AI 時代の「型」は誰のためにあるのか? #frontend_phpcon_do / frontend_phpcon_do_2026
shogogg
1
260
Claude code Orchestra
ozakiomumkj
3
1k
BigQuery の Cross-cloud Lakehouse への歩み
phaya72
2
600
製造業のクラウド活用最適解〜AI,DXを加速するデータ基盤の作り方〜
hamadakoji
0
410
Featured
See All Featured
Making the Leap to Tech Lead
cromwellryan
135
9.9k
Tell your own story through comics
letsgokoyo
1
950
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
270
Unsuck your backbone
ammeep
672
58k
AI: The stuff that nobody shows you
jnunemaker
PRO
8
690
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
The Cost Of JavaScript in 2023
addyosmani
55
10k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
How to Ace a Technical Interview
jacobian
281
24k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
Designing Powerful Visuals for Engaging Learning
tmiket
1
400
Transcript
The Go gopher was designed by Renée French. The gopher
stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. 時の止め方を考える 2024/10/22 Go Connect #3 https://tenn.in/ctxtime
UEDA Takuya / tenntenn 上田拓 也 newmo株式会社 / ソフトウェアエンジニア 一般社団法人
Gophers Japan 代表理事 Google Developers Expert (GDE) / Go Category tenntenn Conference 主催・登壇者 2013年よりGo Conferenceの運営を行う。 2016年、メルカリグループに入社、Goコミュニティへの貢献や採用・社内教育などに従事。 2021年、一般社団法人Gophers Japan設立、代表理事に就任。Google Developers Expert (GDE)に選出。 2022年、株式会社ナレッジワーク入社。ソフトウェアエンジニアおよびGoエンジニアのイネーブルメントに従事。 2024年、newmo株式会社入社。ソフトウェアエンジニアとしてプロダクト開発に従事。 略歴
func Now(context.Context) time.Time { return time.Now() } compilable compile error
1 2 Q
時を止めるとは? ▪ 時刻を使ったコードのテストがしたい • 特定の時刻で固定する package greet func Greet() string
{ now := time.Now() switch h := now.Hour() { case h >= 4 && h <= 9: return "おはよう" case h >= 10 && h <= 16: return "こんにちは" default: return "こんばんは" } }
よくある時の止め方 ▪ 引数に現在時刻を渡す ▪ 関数やインタフェースで差し替える • パッケージ変数やフィールドで持つ ▪ コンテキストに現在時刻を設定する •
context.WithValue関数を使う
やんちゃな時の止め方 - testtime func Now() $GOROOT/src/time/time.go testtime 静的解析 func Now()
$GOPATH/testtime/xxxx/time.go 書き換え go test -overlay 利用 参考:https://tenntenn.dev/ja/posts/2021-07-06-testtime/
静的解析による time.Now関数さがし ▪ GOROOTを取得する • go env GOROOTコマンドを実行 • runtime.GOROOT関数ではコンパイル時のGOROOTになりダメ
▪ パッケージの情報を取得 • (*build.Context).Importメソッドで取得できる • Context.GOROOTフィールドを変更しておく必要がある • パッケージがあるディレクトリのパスを取得 ▪ timeパッケージを構文解析する • parser.ParseDir関数でパースしてAST(抽象構文木を取得) ▪ ソースコードを変更する • ASTを変更する • x/tools/go/astutil.AddImport関数をimport宣言を追加する • go/format.Node関数で出力する
-overlayオプション ▪ ビルド時にファイルを変更する • Overlay JSONのパスを指定する • パスを読み替えることができる ▪ testtimeの実行結果を指定する
• 作成したOverlay JSONのパスをstdoutに表示する • go test -overlay $(testtime)で入れ替えることができる { "Replace": { "/usr/local/go/src/time/time.go": "/Users/tenntenn/go/pkg/testtime/time_go1.16.go" } }
//go:linkname ▪ リンカでシンボルを読み替える • 外部パッケージのシンボル(識別子)を参照できる • Go1.23からstdへのlinknameは禁止になった ◦ ハンドシェイクしてあればOK ▪
自作自演でハンドシェイク • -overlayで上書きするのでハンドシェイクを加える ▪ 共通のsync.Mapを置く • timeパッケージに宣言を追加 • testtimeパッケージがlinknameしておく
ゴルーチンの ID ▪ スタックトレースを自力でパースして取得する • いつ変更されるか分からないので真似してはダメ func goroutineID() string {
var buf [64]byte n := runtime.Stack(buf[:], false) // 10: len("goroutine ") for i := 10; i < n; i++ { if buf[i] == ' ' { return string(buf[10:i]) } } return "" }
testtimeの使い所 ▪ すでにそれなりの規模のプロジェクト • 散らばったtime.Now()を止められない ▪ やんちゃさを暖かく見守れる心 • -overlayとlinknameとゴルーチンのIDを使っている •
テストだし、まぁ最悪いいかの心 • testtime.Overlay()で-overlayしてるか取得できる ◦ 普段はt.Skipで飛ばしてほしい
まじめな時の止め方 - ctxtime ▪ コンテキストに現在時刻を設定する • テストの時だけNow関数を入れ替える ctxtime.Now internal.Now ctxtimetest
呼び出し internal.DefaultNow 呼び出し 入れ替え ctxtimetest.nowForTest 呼び出し 参考:https://tech.newmo.me/entry/2024/09/20/133402
Contextがあれば後でどうにでもなる ▪ newmo入社時に見つけたコード • よくわかってる人(yuki-ito)が書いたと思われるコード ◦ あとで困ることを経験上知っている func Now(_ context.Context)
time.Time { return time.Now().In(time.UTC) }
テストのときだけ挙動を変える ▪ internalを使った3pkg方式 • ctxtimeパッケージはtestingパッケージに依存したくない • internalパッケージに実装を置いておき変更可能にしておく ◦ パッケージ関数ではなく変数にしておく •
ctxtimetestパッケージをimportするとinit関数で入れ替える ◦ testing.Testing関数でテストか判定できる ctxtime.Now internal.Now ctxtimetest 呼び出し internal.DefaultNow 呼び出し 入れ替え ctxtimetest.nowForTest 呼び出し
testid ▪ テストに個別のIDをふる • https://github.com/newmo-oss/testid • (*testing.T).Nameメソッドでテスト名は取得できる ◦ 子テストも含めて •
テスト関数より多くIDが振りたくなることを見越して作成 ▪ コンテキストに仕込む • ミドルウェアやインタセプターで仕込む • ログやらいろんなところでテストが識別できて便利
Linterなきルールはルールではない ▪ time.Now関数を呼び出している箇所を見つける func run(pass *analysis.Pass) (any, error) { in
:= pass.ResultOf[ssainspect.Analyzer].(*ssainspect.Inspector) timenow, _ := analysisutil.ObjectOf(pass, "time", "Now").(*types.Func) if timenow == nil { return nil, nil } for in.Next() { c := in.Cursor() if analysisutil.Called(c.Instr, nil, timenow) { pass.Reportf(c.Instr.Pos(), "do not use %s, use ctxtime.Now", timenow.FullName()) } } return nil, nil }
まとめ ▪ 時を止めたい • テストで時を固定したい ▪ 最初からちゃんと考慮する • コンテキストがあればどうにかなる!
宣伝① まだ資料つくってないので、時を止めたいッッ
宣伝② @newmotech
宣伝③ tenn.in/cfc25