...

Source file src/google.golang.org/grpc/metadata/metadata_test.go

Documentation: google.golang.org/grpc/metadata

     1  /*
     2   *
     3   * Copyright 2014 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    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  		// input
    45  		kv []string
    46  		// output
    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  	// Verify that we lowercase if callers directly modify md
   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  	// ensure modifying result does not modify the value in the context
   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  	// Verify that we lowercase if callers directly modify md
   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  	// Pre-existing metadata
   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  	// No existing metadata
   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  // Old/slow approach to adding metadata to context
   363  func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) {
   364  	// TODO: Add in N=1-100 tests once Go1.6 support is removed.
   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  // Newer/faster approach to adding metadata to context
   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