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