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
140
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
93
まずはイテレータ(range over func)の仕様を学ぼう
tenntenn
PRO
0
480
モチベーションの維持と世代交代から考える持続可能なコミュニティ運営
tenntenn
PRO
0
180
fmfm🤔 fmtパッケージ - tenntenn.go#4
tenntenn
PRO
0
240
あなたの知らない go listコマンド - tenntenn.go#4
tenntenn
PRO
1
1.5k
ライブペアプログラミング その2 - tenntenn.go#3
tenntenn
PRO
0
100
sync.Onceを完全に理解する - tenntenn.go#3
tenntenn
PRO
0
600
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
When Token Pruning is Worse than Random: Understanding Visual Token Information in VLLMs
sansantech
PRO
0
230
Introduction to Bill One Development Engineer
sansan33
PRO
0
490
PdMをやめて、 "プロダクトビルダー"という 働き方に変えました / PdM to Product Builder
shikichee
2
630
Bet AI Day 2026丨AIを「使う」から、AIが「働く」へ ― LayerXが進める「組織AI」の社会実装
layerx
PRO
2
1.5k
KPIだけでは評価できないプロダクトが考えるべき Evalsという第二の評価系 / Beyond KPIs: Evals as a Second Evaluation Framework for Products #PdEConf
aki_iinuma
0
960
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
55k
AIで実装は速くなった。なのにプロダクトは速くならない。職能の壁を越えて価値のフローを設計する
nwiizo
1
1.7k
Genie Code ワークショップ 応用編 / Genie-Code-Workshop-advanced
databricksjapan
PRO
0
260
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
1.3k
振り返りこそエンジニアの本領
negima
0
130
どんな手を使っても絶対間に合わせるスケジューラ
asari194617
0
1.5k
Guerilla InnerSource in enterprises, during the AI hype
onenashev
PRO
0
130
Featured
See All Featured
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
67
57k
Accessibility Awareness
sabderemane
1
190
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
510
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.6k
Scaling GitHub
holman
464
140k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.7k
Darren the Foodie - Storyboard
khoart
PRO
3
3.8k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
260
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
How to train your dragon (web standard)
notwaldorf
97
6.8k
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