Home | History | Annotate | Download | only in regexp
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package regexp
      6 
      7 import (
      8 	"reflect"
      9 	"regexp/syntax"
     10 	"strings"
     11 	"testing"
     12 )
     13 
     14 var runeMergeTests = []struct {
     15 	left, right, merged []rune
     16 	next                []uint32
     17 	leftPC, rightPC     uint32
     18 }{
     19 	{
     20 		// empty rhs
     21 		[]rune{69, 69},
     22 		[]rune{},
     23 		[]rune{69, 69},
     24 		[]uint32{1},
     25 		1, 2,
     26 	},
     27 	{
     28 		// identical runes, identical targets
     29 		[]rune{69, 69},
     30 		[]rune{69, 69},
     31 		[]rune{},
     32 		[]uint32{mergeFailed},
     33 		1, 1,
     34 	},
     35 	{
     36 		// identical runes, different targets
     37 		[]rune{69, 69},
     38 		[]rune{69, 69},
     39 		[]rune{},
     40 		[]uint32{mergeFailed},
     41 		1, 2,
     42 	},
     43 	{
     44 		// append right-first
     45 		[]rune{69, 69},
     46 		[]rune{71, 71},
     47 		[]rune{69, 69, 71, 71},
     48 		[]uint32{1, 2},
     49 		1, 2,
     50 	},
     51 	{
     52 		// append, left-first
     53 		[]rune{71, 71},
     54 		[]rune{69, 69},
     55 		[]rune{69, 69, 71, 71},
     56 		[]uint32{2, 1},
     57 		1, 2,
     58 	},
     59 	{
     60 		// successful interleave
     61 		[]rune{60, 60, 71, 71, 101, 101},
     62 		[]rune{69, 69, 88, 88},
     63 		[]rune{60, 60, 69, 69, 71, 71, 88, 88, 101, 101},
     64 		[]uint32{1, 2, 1, 2, 1},
     65 		1, 2,
     66 	},
     67 	{
     68 		// left surrounds right
     69 		[]rune{69, 74},
     70 		[]rune{71, 71},
     71 		[]rune{},
     72 		[]uint32{mergeFailed},
     73 		1, 2,
     74 	},
     75 	{
     76 		// right surrounds left
     77 		[]rune{69, 74},
     78 		[]rune{68, 75},
     79 		[]rune{},
     80 		[]uint32{mergeFailed},
     81 		1, 2,
     82 	},
     83 	{
     84 		// overlap at interval begin
     85 		[]rune{69, 74},
     86 		[]rune{74, 75},
     87 		[]rune{},
     88 		[]uint32{mergeFailed},
     89 		1, 2,
     90 	},
     91 	{
     92 		// overlap ar interval end
     93 		[]rune{69, 74},
     94 		[]rune{65, 69},
     95 		[]rune{},
     96 		[]uint32{mergeFailed},
     97 		1, 2,
     98 	},
     99 	{
    100 		// overlap from above
    101 		[]rune{69, 74},
    102 		[]rune{71, 74},
    103 		[]rune{},
    104 		[]uint32{mergeFailed},
    105 		1, 2,
    106 	},
    107 	{
    108 		// overlap from below
    109 		[]rune{69, 74},
    110 		[]rune{65, 71},
    111 		[]rune{},
    112 		[]uint32{mergeFailed},
    113 		1, 2,
    114 	},
    115 	{
    116 		// out of order []rune
    117 		[]rune{69, 74, 60, 65},
    118 		[]rune{66, 67},
    119 		[]rune{},
    120 		[]uint32{mergeFailed},
    121 		1, 2,
    122 	},
    123 }
    124 
    125 func TestMergeRuneSet(t *testing.T) {
    126 	for ix, test := range runeMergeTests {
    127 		merged, next := mergeRuneSets(&test.left, &test.right, test.leftPC, test.rightPC)
    128 		if !reflect.DeepEqual(merged, test.merged) {
    129 			t.Errorf("mergeRuneSet :%d (%v, %v) merged\n have\n%v\nwant\n%v", ix, test.left, test.right, merged, test.merged)
    130 		}
    131 		if !reflect.DeepEqual(next, test.next) {
    132 			t.Errorf("mergeRuneSet :%d(%v, %v) next\n have\n%v\nwant\n%v", ix, test.left, test.right, next, test.next)
    133 		}
    134 	}
    135 }
    136 
    137 var onePass = &onePassProg{}
    138 
    139 var onePassTests = []struct {
    140 	re      string
    141 	onePass *onePassProg
    142 }{
    143 	{`^(?:a|(?:a*))$`, notOnePass},
    144 	{`^(?:(a)|(?:a*))$`, notOnePass},
    145 	{`^(?:(?:(?:.(?:$))?))$`, onePass},
    146 	{`^abcd$`, onePass},
    147 	{`^(?:(?:a{0,})*?)$`, onePass},
    148 	{`^(?:(?:a+)*)$`, onePass},
    149 	{`^(?:(?:a|(?:aa)))$`, onePass},
    150 	{`^(?:[^\s\S])$`, onePass},
    151 	{`^(?:(?:a{3,4}){0,})$`, notOnePass},
    152 	{`^(?:(?:(?:a*)+))$`, onePass},
    153 	{`^[a-c]+$`, onePass},
    154 	{`^[a-c]*$`, onePass},
    155 	{`^(?:a*)$`, onePass},
    156 	{`^(?:(?:aa)|a)$`, onePass},
    157 	{`^[a-c]*`, notOnePass},
    158 	{`^...$`, onePass},
    159 	{`^(?:a|(?:aa))$`, onePass},
    160 	{`^a((b))c$`, onePass},
    161 	{`^a.[l-nA-Cg-j]?e$`, onePass},
    162 	{`^a((b))$`, onePass},
    163 	{`^a(?:(b)|(c))c$`, onePass},
    164 	{`^a(?:(b*)|(c))c$`, notOnePass},
    165 	{`^a(?:b|c)$`, onePass},
    166 	{`^a(?:b?|c)$`, onePass},
    167 	{`^a(?:b?|c?)$`, notOnePass},
    168 	{`^a(?:b?|c+)$`, onePass},
    169 	{`^a(?:b+|(bc))d$`, notOnePass},
    170 	{`^a(?:bc)+$`, onePass},
    171 	{`^a(?:[bcd])+$`, onePass},
    172 	{`^a((?:[bcd])+)$`, onePass},
    173 	{`^a(:?b|c)*d$`, onePass},
    174 	{`^.bc(d|e)*$`, onePass},
    175 	{`^(?:(?:aa)|.)$`, notOnePass},
    176 	{`^(?:(?:a{1,2}){1,2})$`, notOnePass},
    177 	{`^l` + strings.Repeat("o", 2<<8) + `ng$`, onePass},
    178 }
    179 
    180 func TestCompileOnePass(t *testing.T) {
    181 	var (
    182 		p   *syntax.Prog
    183 		re  *syntax.Regexp
    184 		err error
    185 	)
    186 	for _, test := range onePassTests {
    187 		if re, err = syntax.Parse(test.re, syntax.Perl); err != nil {
    188 			t.Errorf("Parse(%q) got err:%s, want success", test.re, err)
    189 			continue
    190 		}
    191 		// needs to be done before compile...
    192 		re = re.Simplify()
    193 		if p, err = syntax.Compile(re); err != nil {
    194 			t.Errorf("Compile(%q) got err:%s, want success", test.re, err)
    195 			continue
    196 		}
    197 		onePass = compileOnePass(p)
    198 		if (onePass == notOnePass) != (test.onePass == notOnePass) {
    199 			t.Errorf("CompileOnePass(%q) got %v, expected %v", test.re, onePass, test.onePass)
    200 		}
    201 	}
    202 }
    203 
    204 // TODO(cespare): Unify with onePassTests and rationalize one-pass test cases.
    205 var onePassTests1 = []struct {
    206 	re    string
    207 	match string
    208 }{
    209 	{`^a(/b+(#c+)*)*$`, "a/b#c"}, // golang.org/issue/11905
    210 }
    211 
    212 func TestRunOnePass(t *testing.T) {
    213 	for _, test := range onePassTests1 {
    214 		re, err := Compile(test.re)
    215 		if err != nil {
    216 			t.Errorf("Compile(%q): got err: %s", test.re, err)
    217 			continue
    218 		}
    219 		if re.onepass == notOnePass {
    220 			t.Errorf("Compile(%q): got notOnePass, want one-pass", test.re)
    221 			continue
    222 		}
    223 		if !re.MatchString(test.match) {
    224 			t.Errorf("onepass %q did not match %q", test.re, test.match)
    225 		}
    226 	}
    227 }
    228 
    229 func BenchmarkCompileOnepass(b *testing.B) {
    230 	for _, test := range onePassTests {
    231 		if test.onePass == notOnePass {
    232 			continue
    233 		}
    234 		name := test.re
    235 		if len(name) > 20 {
    236 			name = name[:20] + "..."
    237 		}
    238 		b.Run(name, func(b *testing.B) {
    239 			b.ReportAllocs()
    240 			for i := 0; i < b.N; i++ {
    241 				if _, err := Compile(test.re); err != nil {
    242 					b.Fatal(err)
    243 				}
    244 			}
    245 		})
    246 	}
    247 }
    248