1
18
19 package metadata
20
21 import (
22 "context"
23 "reflect"
24 "strconv"
25 "strings"
26 "testing"
27 "time"
28
29 "google.golang.org/grpc/internal/grpctest"
30 )
31
32 const defaultTestTimeout = 10 * time.Second
33
34 type s struct {
35 grpctest.Tester
36 }
37
38 func Test(t *testing.T) {
39 grpctest.RunSubTests(t, s{})
40 }
41
42 func (s) TestPairsMD(t *testing.T) {
43 for _, test := range []struct {
44
45 kv []string
46
47 md MD
48 }{
49 {[]string{}, MD{}},
50 {[]string{"k1", "v1", "k1", "v2"}, MD{"k1": []string{"v1", "v2"}}},
51 } {
52 md := Pairs(test.kv...)
53 if !reflect.DeepEqual(md, test.md) {
54 t.Fatalf("Pairs(%v) = %v, want %v", test.kv, md, test.md)
55 }
56 }
57 }
58
59 func (s) TestCopy(t *testing.T) {
60 const key, val = "key", "val"
61 orig := Pairs(key, val)
62 cpy := orig.Copy()
63 if !reflect.DeepEqual(orig, cpy) {
64 t.Errorf("copied value not equal to the original, got %v, want %v", cpy, orig)
65 }
66 orig[key][0] = "foo"
67 if v := cpy[key][0]; v != val {
68 t.Errorf("change in original should not affect copy, got %q, want %q", v, val)
69 }
70 }
71
72 func (s) TestJoin(t *testing.T) {
73 for _, test := range []struct {
74 mds []MD
75 want MD
76 }{
77 {[]MD{}, MD{}},
78 {[]MD{Pairs("foo", "bar")}, Pairs("foo", "bar")},
79 {[]MD{Pairs("foo", "bar"), Pairs("foo", "baz")}, Pairs("foo", "bar", "foo", "baz")},
80 {[]MD{Pairs("foo", "bar"), Pairs("foo", "baz"), Pairs("zip", "zap")}, Pairs("foo", "bar", "foo", "baz", "zip", "zap")},
81 } {
82 md := Join(test.mds...)
83 if !reflect.DeepEqual(md, test.want) {
84 t.Errorf("context's metadata is %v, want %v", md, test.want)
85 }
86 }
87 }
88
89 func (s) TestGet(t *testing.T) {
90 for _, test := range []struct {
91 md MD
92 key string
93 wantVals []string
94 }{
95 {md: Pairs("My-Optional-Header", "42"), key: "My-Optional-Header", wantVals: []string{"42"}},
96 {md: Pairs("Header", "42", "Header", "43", "Header", "44", "other", "1"), key: "HEADER", wantVals: []string{"42", "43", "44"}},
97 {md: Pairs("HEADER", "10"), key: "HEADER", wantVals: []string{"10"}},
98 } {
99 vals := test.md.Get(test.key)
100 if !reflect.DeepEqual(vals, test.wantVals) {
101 t.Errorf("value of metadata %v is %v, want %v", test.key, vals, test.wantVals)
102 }
103 }
104 }
105
106 func (s) TestSet(t *testing.T) {
107 for _, test := range []struct {
108 md MD
109 setKey string
110 setVals []string
111 want MD
112 }{
113 {
114 md: Pairs("My-Optional-Header", "42", "other-key", "999"),
115 setKey: "Other-Key",
116 setVals: []string{"1"},
117 want: Pairs("my-optional-header", "42", "other-key", "1"),
118 },
119 {
120 md: Pairs("My-Optional-Header", "42"),
121 setKey: "Other-Key",
122 setVals: []string{"1", "2", "3"},
123 want: Pairs("my-optional-header", "42", "other-key", "1", "other-key", "2", "other-key", "3"),
124 },
125 {
126 md: Pairs("My-Optional-Header", "42"),
127 setKey: "Other-Key",
128 setVals: []string{},
129 want: Pairs("my-optional-header", "42"),
130 },
131 } {
132 test.md.Set(test.setKey, test.setVals...)
133 if !reflect.DeepEqual(test.md, test.want) {
134 t.Errorf("value of metadata is %v, want %v", test.md, test.want)
135 }
136 }
137 }
138
139 func (s) TestAppend(t *testing.T) {
140 for _, test := range []struct {
141 md MD
142 appendKey string
143 appendVals []string
144 want MD
145 }{
146 {
147 md: Pairs("My-Optional-Header", "42"),
148 appendKey: "Other-Key",
149 appendVals: []string{"1"},
150 want: Pairs("my-optional-header", "42", "other-key", "1"),
151 },
152 {
153 md: Pairs("My-Optional-Header", "42"),
154 appendKey: "my-OptIoNal-HeAder",
155 appendVals: []string{"1", "2", "3"},
156 want: Pairs("my-optional-header", "42", "my-optional-header", "1",
157 "my-optional-header", "2", "my-optional-header", "3"),
158 },
159 {
160 md: Pairs("My-Optional-Header", "42"),
161 appendKey: "my-OptIoNal-HeAder",
162 appendVals: []string{},
163 want: Pairs("my-optional-header", "42"),
164 },
165 } {
166 test.md.Append(test.appendKey, test.appendVals...)
167 if !reflect.DeepEqual(test.md, test.want) {
168 t.Errorf("value of metadata is %v, want %v", test.md, test.want)
169 }
170 }
171 }
172
173 func (s) TestDelete(t *testing.T) {
174 for _, test := range []struct {
175 md MD
176 deleteKey string
177 want MD
178 }{
179 {
180 md: Pairs("My-Optional-Header", "42"),
181 deleteKey: "My-Optional-Header",
182 want: Pairs(),
183 },
184 {
185 md: Pairs("My-Optional-Header", "42"),
186 deleteKey: "Other-Key",
187 want: Pairs("my-optional-header", "42"),
188 },
189 {
190 md: Pairs("My-Optional-Header", "42"),
191 deleteKey: "my-OptIoNal-HeAder",
192 want: Pairs(),
193 },
194 } {
195 test.md.Delete(test.deleteKey)
196 if !reflect.DeepEqual(test.md, test.want) {
197 t.Errorf("value of metadata is %v, want %v", test.md, test.want)
198 }
199 }
200 }
201
202 func (s) TestFromIncomingContext(t *testing.T) {
203 md := Pairs(
204 "X-My-Header-1", "42",
205 )
206
207 md["X-INCORRECT-UPPERCASE"] = []string{"foo"}
208 ctx := NewIncomingContext(context.Background(), md)
209
210 result, found := FromIncomingContext(ctx)
211 if !found {
212 t.Fatal("FromIncomingContext must return metadata")
213 }
214 expected := MD{
215 "x-my-header-1": []string{"42"},
216 "x-incorrect-uppercase": []string{"foo"},
217 }
218 if !reflect.DeepEqual(result, expected) {
219 t.Errorf("FromIncomingContext returned %#v, expected %#v", result, expected)
220 }
221
222
223 result["new_key"] = []string{"foo"}
224 result["x-my-header-1"][0] = "mutated"
225
226 result2, found := FromIncomingContext(ctx)
227 if !found {
228 t.Fatal("FromIncomingContext must return metadata")
229 }
230 if !reflect.DeepEqual(result2, expected) {
231 t.Errorf("FromIncomingContext after modifications returned %#v, expected %#v", result2, expected)
232 }
233 }
234
235 func (s) TestValueFromIncomingContext(t *testing.T) {
236 md := Pairs(
237 "X-My-Header-1", "42",
238 "X-My-Header-2", "43-1",
239 "X-My-Header-2", "43-2",
240 "x-my-header-3", "44",
241 )
242
243 md["X-INCORRECT-UPPERCASE"] = []string{"foo"}
244 ctx := NewIncomingContext(context.Background(), md)
245
246 for _, test := range []struct {
247 key string
248 want []string
249 }{
250 {
251 key: "x-my-header-1",
252 want: []string{"42"},
253 },
254 {
255 key: "x-my-header-2",
256 want: []string{"43-1", "43-2"},
257 },
258 {
259 key: "x-my-header-3",
260 want: []string{"44"},
261 },
262 {
263 key: "x-unknown",
264 want: nil,
265 },
266 {
267 key: "x-incorrect-uppercase",
268 want: []string{"foo"},
269 },
270 } {
271 v := ValueFromIncomingContext(ctx, test.key)
272 if !reflect.DeepEqual(v, test.want) {
273 t.Errorf("value of metadata is %v, want %v", v, test.want)
274 }
275 }
276 }
277
278 func (s) TestAppendToOutgoingContext(t *testing.T) {
279
280 tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
281 defer cancel()
282 ctx := NewOutgoingContext(tCtx, Pairs("k1", "v1", "k2", "v2"))
283 ctx = AppendToOutgoingContext(ctx, "k1", "v3")
284 ctx = AppendToOutgoingContext(ctx, "k1", "v4")
285 md, ok := FromOutgoingContext(ctx)
286 if !ok {
287 t.Errorf("Expected MD to exist in ctx, but got none")
288 }
289 want := Pairs("k1", "v1", "k1", "v3", "k1", "v4", "k2", "v2")
290 if !reflect.DeepEqual(md, want) {
291 t.Errorf("context's metadata is %v, want %v", md, want)
292 }
293
294
295 ctx = AppendToOutgoingContext(tCtx, "k1", "v1")
296 md, ok = FromOutgoingContext(ctx)
297 if !ok {
298 t.Errorf("Expected MD to exist in ctx, but got none")
299 }
300 want = Pairs("k1", "v1")
301 if !reflect.DeepEqual(md, want) {
302 t.Errorf("context's metadata is %v, want %v", md, want)
303 }
304 }
305
306 func (s) TestAppendToOutgoingContext_Repeated(t *testing.T) {
307 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
308 defer cancel()
309
310 for i := 0; i < 100; i = i + 2 {
311 ctx1 := AppendToOutgoingContext(ctx, "k", strconv.Itoa(i))
312 ctx2 := AppendToOutgoingContext(ctx, "k", strconv.Itoa(i+1))
313
314 md1, _ := FromOutgoingContext(ctx1)
315 md2, _ := FromOutgoingContext(ctx2)
316
317 if reflect.DeepEqual(md1, md2) {
318 t.Fatalf("md1, md2 = %v, %v; should not be equal", md1, md2)
319 }
320
321 ctx = ctx1
322 }
323 }
324
325 func (s) TestAppendToOutgoingContext_FromKVSlice(t *testing.T) {
326 const k, v = "a", "b"
327 kv := []string{k, v}
328 tCtx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
329 defer cancel()
330 ctx := AppendToOutgoingContext(tCtx, kv...)
331 md, _ := FromOutgoingContext(ctx)
332 if md[k][0] != v {
333 t.Fatalf("md[%q] = %q; want %q", k, md[k], v)
334 }
335 kv[1] = "xxx"
336 md, _ = FromOutgoingContext(ctx)
337 if md[k][0] != v {
338 t.Fatalf("md[%q] = %q; want %q", k, md[k], v)
339 }
340 }
341
342 func TestStringerMD(t *testing.T) {
343 for _, test := range []struct {
344 md MD
345 want []string
346 }{
347 {MD{}, []string{"MD{}"}},
348 {MD{"k1": []string{}}, []string{"MD{k1=[]}"}},
349 {MD{"k1": []string{"v1", "v2"}}, []string{"MD{k1=[v1, v2]}"}},
350 {MD{"k1": []string{"v1"}}, []string{"MD{k1=[v1]}"}},
351 {MD{"k1": []string{"v1", "v2"}, "k2": []string{}, "k3": []string{"1", "2", "3"}}, []string{"MD{", "k1=[v1, v2]", "k2=[]", "k3=[1, 2, 3]", "}"}},
352 } {
353 got := test.md.String()
354 for _, want := range test.want {
355 if !strings.Contains(got, want) {
356 t.Fatalf("Metadata string %q is missing %q", got, want)
357 }
358 }
359 }
360 }
361
362
363 func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) {
364
365 const num = 10
366 for n := 0; n < b.N; n++ {
367 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
368 defer cancel()
369 for i := 0; i < num; i++ {
370 md, _ := FromOutgoingContext(ctx)
371 NewOutgoingContext(ctx, Join(Pairs("k1", "v1", "k2", "v2"), md))
372 }
373 }
374 }
375
376
377 func BenchmarkAppendToOutgoingContext(b *testing.B) {
378 const num = 10
379 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
380 defer cancel()
381 for n := 0; n < b.N; n++ {
382 for i := 0; i < num; i++ {
383 ctx = AppendToOutgoingContext(ctx, "k1", "v1", "k2", "v2")
384 }
385 }
386 }
387
388 func BenchmarkFromOutgoingContext(b *testing.B) {
389 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
390 defer cancel()
391 ctx = NewOutgoingContext(ctx, MD{"k3": {"v3", "v4"}})
392 ctx = AppendToOutgoingContext(ctx, "k1", "v1", "k2", "v2")
393
394 for n := 0; n < b.N; n++ {
395 FromOutgoingContext(ctx)
396 }
397 }
398
399 func BenchmarkFromIncomingContext(b *testing.B) {
400 md := Pairs("X-My-Header-1", "42")
401 ctx := NewIncomingContext(context.Background(), md)
402 b.ResetTimer()
403 for n := 0; n < b.N; n++ {
404 FromIncomingContext(ctx)
405 }
406 }
407
408 func BenchmarkValueFromIncomingContext(b *testing.B) {
409 md := Pairs("X-My-Header-1", "42")
410 ctx := NewIncomingContext(context.Background(), md)
411
412 b.Run("key-found", func(b *testing.B) {
413 for n := 0; n < b.N; n++ {
414 result := ValueFromIncomingContext(ctx, "x-my-header-1")
415 if len(result) != 1 {
416 b.Fatal("ensures not optimized away")
417 }
418 }
419 })
420
421 b.Run("key-not-found", func(b *testing.B) {
422 for n := 0; n < b.N; n++ {
423 result := ValueFromIncomingContext(ctx, "key-not-found")
424 if len(result) != 0 {
425 b.Fatal("ensures not optimized away")
426 }
427 }
428 })
429 }
430
View as plain text