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
yubessy
May 29, 2017
Programming
2.5k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
正規表現のテストカバレッジを測りたかった話
※社内LT用につくったもの
yubessy
May 29, 2017
More Decks by yubessy
See All by yubessy
DDIA (Designing Data-Intensive Applications) はいいぞ
yubessy
0
1.7k
Introduction to CircleCI
yubessy
1
150
Docker Hands-on
yubessy
0
140
Resource Polymorphism
yubessy
0
330
不動点コンビネータ?
yubessy
0
330
とりあえず機械学習したかった
yubessy
0
360
Scala Native
yubessy
0
250
Type Erasure と Reflection のはなし
yubessy
1
520
量子暗号
yubessy
0
270
Other Decks in Programming
See All in Programming
ActionController meets Protocol Buffers
sorah
1
780
Getting Started with Vibe Coding
miccheng
0
180
動画プレイヤーはなぜカクつくのか 〜Flutterのレンダリングとネイティブの境界を解剖する〜
naruogram
0
320
LLM 101
dalinaum
0
460
SFCで実現する病院・医療システムの動的組み立て式UI
koukimiura
1
170
AIがコードを書いてくれる時代、我々は何を確かめるのか
yosuke_furukawa
PRO
13
5.6k
複数ドメインを一つのアプリに統合するFlutterの設計と運用_FlutterKaigi2026登壇資料(須永高弘)
takahirosunaga
0
140
AI-Powered Domain-Driven Design
ziobrando
0
380
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
4
3.7k
SODA - FACT BOOK(EN)
sodainc
1
200
Python Lambdaにおけるテストしやすい設計とAI支援の活用 / Designing Testable Python Lambda Functions with AI
kitano_yuichi
1
290
Spec-Driven Development - Why Vaadin Is the Perfect Fit
simas
PRO
0
220
Featured
See All Featured
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
340
How to make the Groovebox
asonas
2
2.5k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
560
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.2k
The Pragmatic Product Professional
lauravandoore
37
7.5k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.6k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.7k
Fireside Chat
paigeccino
43
4.1k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
390
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
890
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
680
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.2k
Transcript
正規表現のテストカバレッジを 測りたかった話 @yubessy 0x64 物語 Reboot #03 " テスト"
正規表現書いてますか?
テスト書いてますか?
正規表現のテスト ↓ これをテストしたい /[a-z]+\.(co|ne)\.jp/ たぶんこんなかんじ assert("hoge.co.jp" =~ /[a-z]+\.(co|ne)\.jp/) assert("hoge.ne.jp" =~
/[a-z]+\.(co|ne)\.jp/)
やりたいこと 正規表現のテストカバレッジを測りたい /[a-z]+\.(co|ne)\.jp/ ↓ カバレッジが十分でない例 assert("hoge.co.jp" =~ /[a-z]+\.(co|ne)\.jp/) # "hoge.ne.jp"
は? こういうのを数値化したい
Q. それくらい見ればわかるやん?
A. (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_ `{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\ x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0 -9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0 -9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3 }(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0 -9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\ [\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
A. RFC5322 準拠の email adress (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_ `{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\ x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0 -9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0 -9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3
}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0 -9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\ [\x01-\x09\x0b\x0c\x0e-\x7f])+)\]) https://emailregex.com/
どうやってカバレッジを測るか? 正規表現 = 状態機械 カバレッジ(C0) = 全てのパスを1 度以上通ったか ※C1 以上はとりあえず今回は無視
ちなみにさっきのやつ
ここで問題発生 大体の言語では正規表現はライブラリとして提供 -> 内部実装はラップされていて見えない -> 各パスを通った・ 通らないを知るすべがない -> 処理系に手を入れる
手頃な処理系を探す旅 PCRE やばい java.util.regex つらい Oniguruma / Onigumo でかい Rust
おしい( 僕の力では読めない) Golang <- !!!
Golang の正規表現エンジン NFA( 非決定性有限オー トマン) ベー ス アルゴリズムが理解しやすい 計算量が文字数に対して線形 平均的には遅い
Pure Go で実装 自分でも読める コー ド量が手頃( エンジン部分は数100 行) https://github.com/golang/go/tree/master/src/regexp
Golang の正規表現エンジン re := regexp.MustCompile("/[a-z]+\.(co|ne)\.jp/") re.MatchString("hoge.co.jp") 内部では 1. 正規表現の構文木を作成 (
syntax/parse.go ) 2. 構文木からプログラムを作成 ( syntax/compile.go ) 3. 文字列を入力としてプログラムを実行 ( exec.go )
正規表現プログラムの内部表現 プログラム = 文字を1コずつ処理する命令の列 syntax/prog.go type Prog struct { Inst
[]Inst ... } type Inst struct { Op InstOp // 命令の種類 Out uint32 // 成功時のジャンプ先の命令番号 Arg uint32 // 失敗時のジャンプ先の命令番号( など) Rune []rune // マッチする文字( 文字マッチ命令のみ) }
プログラムの内部表現 0: InstFail 0 0 [] // 失敗( 最初はスキップ) 1:
InstRune 2 0 ['a', 'z'] // 'a' - 'z' 2: InstAlt 1 3 [] // 命令1, 3 のいずれか 3: InstRune1 4 0 ['.'] // '.' 4: InstCapture 9 2 [] // グルー ピング開始 5: InstRune1 6 0 ['c'] // 'c' 6: InstRune1 10 0 ['o'] // 'o' 7: InstRune1 8 0 ['n'] // 'n' 8: InstRune1 10 0 ['e'] // 'e' 9: InstAlt 5 7 [] // 命令5, 7 のいずれか 10: InstCapture 11 3 [] // グルー ピング終了 11: InstRune1 12 0 ['.'] // '.' 12: InstRune1 13 0 ['j'] // 'j' 13: InstRune1 14 0 ['p'] // 'p' 14: InstMatch 0 0 [] // 成功
カバレッジの計算 やりたいこと = 正規表現のカバレッジを測る -> 各命令にテスト済みフラグを用意 type Inst struct {
Op InstOp Out uint32 Arg uint32 Rune []rune Flag bool // テスト済みフラグ <- new! }
カバレッジの計算 マッチの実行時に通った命令をマー キングしておく // 文字マッチ命令を1 つ実行する関数 (exec.go) func (m *machine)
step(...) { ... switch i.Op { ... case syntax.InstRune: add = i.MatchRune(c) case syntax.InstRune1: add = c == i.Rune[0] ... if add { i.Flag = true // マッチが成功したらフラグを立てる } ... }
カバレッジの計算 テストケー スを食わせてフラグの立った命令を数える func (re *Regexp) Coverage() (int, int) {
s, a := 0, 0 for _, x := range re.prog.Inst { if x.Op == syntax.InstRune || ... { a++ if x.Flag { s++ } } } return s, a }
カバレッジの計算 できた!!! func main() { re := regexp.MustCompile(`[a-z]+\.(co|ne)\.jp`) re.MatchString("hoge.co.jp") c,
a := re.Coverage() fmt.Printf("%d / %d", c, a) } 7 / 9 ※ 文字マッチ命令の 成功 / 全部 を数えた場合
まとめ 正規表現のカバレッジは( 頑張れば) 測れる Golang の正規表現実装は読みやすい テスト回のはずが正規表現回になった