Source file
src/net/http/routing_index_test.go
1
2
3
4
5 package http
6
7 import (
8 "fmt"
9 "slices"
10 "strings"
11 "testing"
12 )
13
14 func TestIndex(t *testing.T) {
15
16
17
18 patterns := generatePatterns()
19 var idx routingIndex
20 for i, pat := range patterns {
21 got := indexConflicts(pat, &idx)
22 want := trueConflicts(pat, patterns[:i])
23 if !slices.Equal(got, want) {
24 t.Fatalf("%q:\ngot %q\nwant %q", pat, got, want)
25 }
26 idx.addPattern(pat)
27 }
28 }
29
30 func trueConflicts(pat *pattern, pats []*pattern) []string {
31 var s []string
32 for _, p := range pats {
33 if pat.conflictsWith(p) {
34 s = append(s, p.String())
35 }
36 }
37 slices.Sort(s)
38 return s
39 }
40
41 func indexConflicts(pat *pattern, idx *routingIndex) []string {
42 var s []string
43 idx.possiblyConflictingPatterns(pat, func(p *pattern) error {
44 if pat.conflictsWith(p) {
45 s = append(s, p.String())
46 }
47 return nil
48 })
49 slices.Sort(s)
50 return slices.Compact(s)
51 }
52
53
54
55 func generatePatterns() []*pattern {
56 var pats []*pattern
57
58 collect := func(s string) {
59
60 var b strings.Builder
61 wc := 0
62 for {
63 i := strings.Index(s, "{x}")
64 if i < 0 {
65 b.WriteString(s)
66 break
67 }
68 b.WriteString(s[:i])
69 fmt.Fprintf(&b, "{x%d}", wc)
70 wc++
71 s = s[i+3:]
72 }
73 pat, err := parsePattern(b.String())
74 if err != nil {
75 panic(err)
76 }
77 pats = append(pats, pat)
78 }
79
80 var (
81 methods = []string{"", "GET ", "HEAD ", "POST "}
82 hosts = []string{"", "h1", "h2"}
83 segs = []string{"/a", "/b", "/{x}"}
84 finalSegs = []string{"/a", "/b", "/{f}", "/{m...}", "/{$}"}
85 )
86
87 g := genConcat(
88 genChoice(methods),
89 genChoice(hosts),
90 genStar(3, genChoice(segs)),
91 genChoice(finalSegs))
92 g(collect)
93 return pats
94 }
95
96
97
98 type generator func(collect func(string))
99
100
101 func genConst(s string) generator {
102 return func(collect func(string)) {
103 collect(s)
104 }
105 }
106
107
108 func genChoice(choices []string) generator {
109 return func(collect func(string)) {
110 for _, c := range choices {
111 collect(c)
112 }
113 }
114 }
115
116
117
118 func genConcat2(g1, g2 generator) generator {
119 return func(collect func(string)) {
120 g1(func(s1 string) {
121 g2(func(s2 string) {
122 collect(s1 + s2)
123 })
124 })
125 }
126 }
127
128
129 func genConcat(gs ...generator) generator {
130 if len(gs) == 0 {
131 return genConst("")
132 }
133 return genConcat2(gs[0], genConcat(gs[1:]...))
134 }
135
136
137 func genRepeat(n int, g generator) generator {
138 if n == 0 {
139 return genConst("")
140 }
141 return genConcat(g, genRepeat(n-1, g))
142 }
143
144
145
146 func genStar(max int, g generator) generator {
147 return func(collect func(string)) {
148 for i := 0; i <= max; i++ {
149 genRepeat(i, g)(collect)
150 }
151 }
152 }
153
154 func BenchmarkMultiConflicts(b *testing.B) {
155
156 const nMultis = 1000
157 var pats []*pattern
158 for i := 0; i < nMultis; i++ {
159 pats = append(pats, mustParsePattern(b, fmt.Sprintf("/a/b/{x}/d%d/", i)))
160 }
161 b.ResetTimer()
162 for i := 0; i < b.N; i++ {
163 var idx routingIndex
164 for _, p := range pats {
165 got := indexConflicts(p, &idx)
166 if len(got) != 0 {
167 b.Fatalf("got %d conflicts, want 0", len(got))
168 }
169 idx.addPattern(p)
170 }
171 if i == 0 {
172
173 if g, w := len(idx.multis), nMultis; g != w {
174 b.Fatalf("got %d multis, want %d", g, w)
175 }
176 }
177 }
178 }
179
View as plain text