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
yubessy
May 29, 2017
Programming
2.4k
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.6k
Introduction to CircleCI
yubessy
1
130
Docker Hands-on
yubessy
0
120
Resource Polymorphism
yubessy
0
310
不動点コンビネータ?
yubessy
0
320
とりあえず機械学習したかった
yubessy
0
350
Scala Native
yubessy
0
240
Type Erasure と Reflection のはなし
yubessy
1
490
量子暗号
yubessy
0
250
Other Decks in Programming
See All in Programming
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.6k
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
250
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
210
2年かけて Deno に DOMMatrix を実装した話 / How I implemented DOMMatrix in Deno over two years
petamoriken
0
210
メールのエイリアス機能を履き違えない
isshinfunada
0
230
React本体のコードリーディング
high_g_engineer
1
140
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
540
仕様駆動開発の消費期限
watany
17
7.4k
Go 1.27 における memory allocation の高速化
andpad
0
120
What's New in Android 2026
veronikapj
0
250
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
2
260
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
380
Featured
See All Featured
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
480
Un-Boring Meetings
codingconduct
0
380
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
What's in a price? How to price your products and services
michaelherold
247
13k
So, you think you're a good person
axbom
PRO
2
2.1k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
64
56k
The Curious Case for Waylosing
cassininazir
1
450
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
200
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.5k
Chasing Engaging Ingredients in Design
codingconduct
0
260
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
230
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 の正規表現実装は読みやすい テスト回のはずが正規表現回になった