Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
時の止め方を考える
Search
tenntenn - Takuya Ueda
PRO
October 23, 2024
Technology
150
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
94
まずはイテレータ(range over func)の仕様を学ぼう
tenntenn
PRO
0
480
モチベーションの維持と世代交代から考える持続可能なコミュニティ運営
tenntenn
PRO
0
180
fmfm🤔 fmtパッケージ - tenntenn.go#4
tenntenn
PRO
0
250
あなたの知らない go listコマンド - tenntenn.go#4
tenntenn
PRO
1
1.6k
ライブペアプログラミング その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
700
io/fsパッケージを読む - tenntenn.go#2
tenntenn
PRO
0
290
Other Decks in Technology
See All in Technology
Goodbye ShellScript, Hello File-based App
shunsock
0
380
株式会社シーエーシー エンジニア向け会社紹介資料
cac
0
57k
AIで社員の自主発信に広報目線を組み込む
_mossann_t
0
130
登壇の自信を奪う3匹のオバケ / 3 Ghosts That Rob You of Your Confidence in Public Speaking
pauli
9
1k
【技術的負債conf】事業成長に伴う技術的負債の説明責任とAIによるモニタリング、認知的負債について
i35_267
3
1.8k
HHKBエバンジェリストになる方法
941
0
110
AIによるクリエイティブ生成を行う上での試行錯誤
plaidtech
PRO
0
160
ユーザー価値を届け続けるためにウォンテッドリーが大切にしている文化
kotaminato
0
180
AIを活用するために決めた "やらないこと" - 価値に注目する / Not betting on AI
soudai
PRO
3
560
SREは、MCPとAutopilotをこう使え!
kazumax55
3
910
AIエージェントの一手は 誰も見ていない - Falco拡張OSS「Prempti」とeBPFで サーバーレス実行基盤を二層防御する
keitah
0
310
俺の仕事は AIに奪われないし、たぶんその BIも要らない
hikaruri
0
520
Featured
See All Featured
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Docker and Python
trallard
47
4.2k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
What's in a price? How to price your products and services
michaelherold
247
13k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
470
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.9k
From π to Pie charts
rasagy
1
370
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
290
Rails Girls Zürich Keynote
gr2m
96
14k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
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