gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/resolver/xds_resolver_test.go (about)

     1  /*
     2   *
     3   * Copyright 2019 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 resolver
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"net/url"
    25  	"reflect"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"gitee.com/ks-custle/core-gm/grpc/codes"
    31  	"gitee.com/ks-custle/core-gm/grpc/credentials/insecure"
    32  	xdscreds "gitee.com/ks-custle/core-gm/grpc/credentials/xds"
    33  	"gitee.com/ks-custle/core-gm/grpc/internal"
    34  	"gitee.com/ks-custle/core-gm/grpc/internal/envconfig"
    35  	"gitee.com/ks-custle/core-gm/grpc/internal/grpcrand"
    36  	"gitee.com/ks-custle/core-gm/grpc/internal/grpctest"
    37  	iresolver "gitee.com/ks-custle/core-gm/grpc/internal/resolver"
    38  	"gitee.com/ks-custle/core-gm/grpc/internal/testutils"
    39  	"gitee.com/ks-custle/core-gm/grpc/internal/wrr"
    40  	"gitee.com/ks-custle/core-gm/grpc/metadata"
    41  	"gitee.com/ks-custle/core-gm/grpc/resolver"
    42  	"gitee.com/ks-custle/core-gm/grpc/serviceconfig"
    43  	"gitee.com/ks-custle/core-gm/grpc/status"
    44  	_ "gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/cdsbalancer" // To parse LB config
    45  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/clustermanager"
    46  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/balancer/ringhash"
    47  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/httpfilter"
    48  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/httpfilter/router"
    49  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeclient"
    50  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient"
    51  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/bootstrap"
    52  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource"
    53  	xxhash "github.com/cespare/xxhash/v2"
    54  	"github.com/google/go-cmp/cmp"
    55  )
    56  
    57  const (
    58  	targetStr               = "target"
    59  	routeStr                = "route"
    60  	cluster                 = "cluster"
    61  	defaultTestTimeout      = 1 * time.Second
    62  	defaultTestShortTimeout = 100 * time.Microsecond
    63  )
    64  
    65  // Endpoint is deprecated, use URL.Path instead.
    66  // var target = resolver.Target{Endpoint: targetStr, URL: url.URL{Scheme: "xds", Path: "/" + targetStr}}
    67  var target = resolver.Target{URL: url.URL{Scheme: "xds", Path: "/" + targetStr}}
    68  
    69  var routerFilter = xdsresource.HTTPFilter{Name: "rtr", Filter: httpfilter.Get(router.TypeURL)}
    70  var routerFilterList = []xdsresource.HTTPFilter{routerFilter}
    71  
    72  type s struct {
    73  	grpctest.Tester
    74  }
    75  
    76  func Test(t *testing.T) {
    77  	grpctest.RunSubTests(t, s{})
    78  }
    79  
    80  func (s) TestRegister(t *testing.T) {
    81  	b := resolver.Get(xdsScheme)
    82  	if b == nil {
    83  		t.Errorf("scheme %v is not registered", xdsScheme)
    84  	}
    85  }
    86  
    87  // testClientConn is a fake implemetation of resolver.ClientConn. All is does
    88  // is to store the state received from the resolver locally and signal that
    89  // event through a channel.
    90  type testClientConn struct {
    91  	resolver.ClientConn
    92  	stateCh *testutils.Channel
    93  	errorCh *testutils.Channel
    94  }
    95  
    96  func (t *testClientConn) UpdateState(s resolver.State) error {
    97  	t.stateCh.Send(s)
    98  	return nil
    99  }
   100  
   101  func (t *testClientConn) ReportError(err error) {
   102  	t.errorCh.Send(err)
   103  }
   104  
   105  func (t *testClientConn) ParseServiceConfig(jsonSC string) *serviceconfig.ParseResult {
   106  	return internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(jsonSC)
   107  }
   108  
   109  func newTestClientConn() *testClientConn {
   110  	return &testClientConn{
   111  		stateCh: testutils.NewChannel(),
   112  		errorCh: testutils.NewChannel(),
   113  	}
   114  }
   115  
   116  // TestResolverBuilder tests the xdsResolverBuilder's Build method with
   117  // different parameters.
   118  func (s) TestResolverBuilder(t *testing.T) {
   119  	tests := []struct {
   120  		name          string
   121  		xdsClientFunc func() (xdsclient.XDSClient, error)
   122  		target        resolver.Target
   123  		wantErr       bool
   124  	}{
   125  		{
   126  			name: "simple-good",
   127  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   128  				return fakeclient.NewClient(), nil
   129  			},
   130  			target:  target,
   131  			wantErr: false,
   132  		},
   133  		{
   134  			name: "newXDSClient-throws-error",
   135  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   136  				return nil, errors.New("newXDSClient-throws-error")
   137  			},
   138  			target:  target,
   139  			wantErr: true,
   140  		},
   141  		{
   142  			name: "authority not defined in bootstrap",
   143  			xdsClientFunc: func() (xdsclient.XDSClient, error) {
   144  				c := fakeclient.NewClient()
   145  				c.SetBootstrapConfig(&bootstrap.Config{
   146  					ClientDefaultListenerResourceNameTemplate: "%s",
   147  					Authorities: map[string]*bootstrap.Authority{
   148  						"test-authority": {
   149  							ClientListenerResourceNameTemplate: "xdstp://test-authority/%s",
   150  						},
   151  					},
   152  				})
   153  				return c, nil
   154  			},
   155  			target: resolver.Target{
   156  				URL: url.URL{
   157  					Host: "non-existing-authority",
   158  					Path: "/" + targetStr,
   159  				},
   160  			},
   161  			wantErr: true,
   162  		},
   163  	}
   164  	for _, test := range tests {
   165  		t.Run(test.name, func(t *testing.T) {
   166  			// Fake out the xdsClient creation process by providing a fake.
   167  			oldClientMaker := newXDSClient
   168  			newXDSClient = test.xdsClientFunc
   169  			defer func() {
   170  				newXDSClient = oldClientMaker
   171  			}()
   172  
   173  			builder := resolver.Get(xdsScheme)
   174  			if builder == nil {
   175  				t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   176  			}
   177  
   178  			r, err := builder.Build(test.target, newTestClientConn(), resolver.BuildOptions{})
   179  			if (err != nil) != test.wantErr {
   180  				t.Fatalf("builder.Build(%v) returned err: %v, wantErr: %v", target, err, test.wantErr)
   181  			}
   182  			if err != nil {
   183  				// This is the case where we expect an error and got it.
   184  				return
   185  			}
   186  			r.Close()
   187  		})
   188  	}
   189  }
   190  
   191  // TestResolverBuilder_xdsCredsBootstrapMismatch tests the case where an xds
   192  // resolver is built with xds credentials being specified by the user. The
   193  // bootstrap file does not contain any certificate provider configuration
   194  // though, and therefore we expect the resolver build to fail.
   195  func (s) TestResolverBuilder_xdsCredsBootstrapMismatch(t *testing.T) {
   196  	// Fake out the xdsClient creation process by providing a fake, which does
   197  	// not have any certificate provider configuration.
   198  	fc := fakeclient.NewClient()
   199  	fc.SetBootstrapConfig(&bootstrap.Config{})
   200  	oldClientMaker := newXDSClient
   201  	newXDSClient = func() (xdsclient.XDSClient, error) {
   202  		return fc, nil
   203  	}
   204  	defer func() { newXDSClient = oldClientMaker }()
   205  	defer func() {
   206  		select {
   207  		case <-time.After(defaultTestTimeout):
   208  			t.Fatalf("timeout waiting for close")
   209  		case <-fc.Closed.Done():
   210  		}
   211  	}()
   212  
   213  	builder := resolver.Get(xdsScheme)
   214  	if builder == nil {
   215  		t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   216  	}
   217  
   218  	// Create xds credentials to be passed to resolver.Build().
   219  	creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
   220  	if err != nil {
   221  		t.Fatalf("xds.NewClientCredentials() failed: %v", err)
   222  	}
   223  
   224  	// Since the fake xds client is not configured with any certificate provider
   225  	// configs, and we are specifying xds credentials in the call to
   226  	// resolver.Build(), we expect it to fail.
   227  	if _, err := builder.Build(target, newTestClientConn(), resolver.BuildOptions{DialCreds: creds}); err == nil {
   228  		t.Fatal("builder.Build() succeeded when expected to fail")
   229  	}
   230  }
   231  
   232  type setupOpts struct {
   233  	bootstrapC *bootstrap.Config
   234  	target     resolver.Target
   235  }
   236  
   237  func testSetup(t *testing.T, opts setupOpts) (*xdsResolver, *fakeclient.Client, *testClientConn, func()) {
   238  	t.Helper()
   239  
   240  	fc := fakeclient.NewClient()
   241  	if opts.bootstrapC != nil {
   242  		fc.SetBootstrapConfig(opts.bootstrapC)
   243  	}
   244  	oldClientMaker := newXDSClient
   245  	newXDSClient = func() (xdsclient.XDSClient, error) {
   246  		return fc, nil
   247  	}
   248  	cancel := func() {
   249  		// Make sure the xDS client is closed, in all (successful or failed)
   250  		// cases.
   251  		select {
   252  		case <-time.After(defaultTestTimeout):
   253  			t.Fatalf("timeout waiting for close")
   254  		case <-fc.Closed.Done():
   255  		}
   256  		newXDSClient = oldClientMaker
   257  	}
   258  	builder := resolver.Get(xdsScheme)
   259  	if builder == nil {
   260  		t.Fatalf("resolver.Get(%v) returned nil", xdsScheme)
   261  	}
   262  
   263  	tcc := newTestClientConn()
   264  	r, err := builder.Build(opts.target, tcc, resolver.BuildOptions{})
   265  	if err != nil {
   266  		t.Fatalf("builder.Build(%v) returned err: %v", target, err)
   267  	}
   268  	return r.(*xdsResolver), fc, tcc, func() {
   269  		r.Close()
   270  		cancel()
   271  	}
   272  }
   273  
   274  // waitForWatchListener waits for the WatchListener method to be called on the
   275  // xdsClient within a reasonable amount of time, and also verifies that the
   276  // watch is called with the expected target.
   277  func waitForWatchListener(ctx context.Context, t *testing.T, xdsC *fakeclient.Client, wantTarget string) {
   278  	t.Helper()
   279  
   280  	gotTarget, err := xdsC.WaitForWatchListener(ctx)
   281  	if err != nil {
   282  		t.Fatalf("xdsClient.WatchService failed with error: %v", err)
   283  	}
   284  	if gotTarget != wantTarget {
   285  		t.Fatalf("xdsClient.WatchService() called with target: %v, want %v", gotTarget, wantTarget)
   286  	}
   287  }
   288  
   289  // waitForWatchRouteConfig waits for the WatchRoute method to be called on the
   290  // xdsClient within a reasonable amount of time, and also verifies that the
   291  // watch is called with the expected target.
   292  func waitForWatchRouteConfig(ctx context.Context, t *testing.T, xdsC *fakeclient.Client, wantTarget string) {
   293  	t.Helper()
   294  
   295  	gotTarget, err := xdsC.WaitForWatchRouteConfig(ctx)
   296  	if err != nil {
   297  		t.Fatalf("xdsClient.WatchService failed with error: %v", err)
   298  	}
   299  	if gotTarget != wantTarget {
   300  		t.Fatalf("xdsClient.WatchService() called with target: %v, want %v", gotTarget, wantTarget)
   301  	}
   302  }
   303  
   304  // TestXDSResolverResourceNameToWatch tests that the correct resource name is
   305  // used to watch for the service. This covers cases with different bootstrap
   306  // config, and different authority.
   307  func (s) TestXDSResolverResourceNameToWatch(t *testing.T) {
   308  	tests := []struct {
   309  		name   string
   310  		bc     *bootstrap.Config
   311  		target resolver.Target
   312  		want   string
   313  	}{
   314  		{
   315  			name: "default %s old style",
   316  			bc: &bootstrap.Config{
   317  				ClientDefaultListenerResourceNameTemplate: "%s",
   318  			},
   319  			target: resolver.Target{
   320  				URL: url.URL{Path: "/" + targetStr},
   321  			},
   322  			want: targetStr,
   323  		},
   324  		{
   325  			name: "old style no percent encoding",
   326  			bc: &bootstrap.Config{
   327  				ClientDefaultListenerResourceNameTemplate: "/path/to/%s",
   328  			},
   329  			target: resolver.Target{
   330  				URL: url.URL{Path: "/" + targetStr},
   331  			},
   332  			want: "/path/to/" + targetStr,
   333  		},
   334  		{
   335  			name: "new style with %s",
   336  			bc: &bootstrap.Config{
   337  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   338  				Authorities: nil,
   339  			},
   340  			target: resolver.Target{
   341  				URL: url.URL{Path: "/0.0.0.0:8080"},
   342  			},
   343  			want: "xdstp://authority.com/0.0.0.0:8080",
   344  		},
   345  		{
   346  			name: "new style percent encoding",
   347  			bc: &bootstrap.Config{
   348  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   349  				Authorities: nil,
   350  			},
   351  			target: resolver.Target{
   352  				URL: url.URL{Path: "/[::1]:8080"},
   353  			},
   354  			want: "xdstp://authority.com/%5B::1%5D:8080",
   355  		},
   356  		{
   357  			name: "new style different authority",
   358  			bc: &bootstrap.Config{
   359  				ClientDefaultListenerResourceNameTemplate: "xdstp://authority.com/%s",
   360  				Authorities: map[string]*bootstrap.Authority{
   361  					"test-authority": {
   362  						ClientListenerResourceNameTemplate: "xdstp://test-authority/%s",
   363  					},
   364  				},
   365  			},
   366  			target: resolver.Target{
   367  				URL: url.URL{
   368  					Host: "test-authority",
   369  					Path: "/" + targetStr,
   370  				},
   371  			},
   372  			want: "xdstp://test-authority/" + targetStr,
   373  		},
   374  	}
   375  	for _, tt := range tests {
   376  		t.Run(tt.name, func(t *testing.T) {
   377  			xdsR, xdsC, _, cancel := testSetup(t, setupOpts{
   378  				bootstrapC: tt.bc,
   379  				target:     tt.target,
   380  			})
   381  			defer cancel()
   382  			defer xdsR.Close()
   383  
   384  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   385  			defer cancel()
   386  			waitForWatchListener(ctx, t, xdsC, tt.want)
   387  		})
   388  	}
   389  }
   390  
   391  // TestXDSResolverWatchCallbackAfterClose tests the case where a service update
   392  // from the underlying xdsClient is received after the resolver is closed.
   393  func (s) TestXDSResolverWatchCallbackAfterClose(t *testing.T) {
   394  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   395  	defer cancel()
   396  
   397  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   398  	defer cancel()
   399  	waitForWatchListener(ctx, t, xdsC, targetStr)
   400  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   401  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   402  
   403  	// Call the watchAPI callback after closing the resolver, and make sure no
   404  	// update is triggerred on the ClientConn.
   405  	xdsR.Close()
   406  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   407  		VirtualHosts: []*xdsresource.VirtualHost{
   408  			{
   409  				Domains: []string{targetStr},
   410  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   411  			},
   412  		},
   413  	}, nil)
   414  
   415  	if gotVal, gotErr := tcc.stateCh.Receive(ctx); gotErr != context.DeadlineExceeded {
   416  		t.Fatalf("ClientConn.UpdateState called after xdsResolver is closed: %v", gotVal)
   417  	}
   418  }
   419  
   420  // TestXDSResolverCloseClosesXDSClient tests that the XDS resolver's Close
   421  // method closes the XDS client.
   422  func (s) TestXDSResolverCloseClosesXDSClient(t *testing.T) {
   423  	xdsR, xdsC, _, cancel := testSetup(t, setupOpts{target: target})
   424  	defer cancel()
   425  	xdsR.Close()
   426  	if !xdsC.Closed.HasFired() {
   427  		t.Fatalf("xds client not closed by xds resolver Close method")
   428  	}
   429  }
   430  
   431  // TestXDSResolverBadServiceUpdate tests the case the xdsClient returns a bad
   432  // service update.
   433  func (s) TestXDSResolverBadServiceUpdate(t *testing.T) {
   434  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   435  	defer xdsR.Close()
   436  	defer cancel()
   437  
   438  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   439  	defer cancel()
   440  	waitForWatchListener(ctx, t, xdsC, targetStr)
   441  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   442  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   443  
   444  	// Invoke the watchAPI callback with a bad service update and wait for the
   445  	// ReportError method to be called on the ClientConn.
   446  	suErr := errors.New("bad serviceupdate")
   447  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   448  
   449  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr {
   450  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr)
   451  	}
   452  }
   453  
   454  // TestXDSResolverGoodServiceUpdate tests the happy case where the resolver
   455  // gets a good service update from the xdsClient.
   456  func (s) TestXDSResolverGoodServiceUpdate(t *testing.T) {
   457  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   458  	defer xdsR.Close()
   459  	defer cancel()
   460  
   461  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   462  	defer cancel()
   463  	waitForWatchListener(ctx, t, xdsC, targetStr)
   464  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   465  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   466  	defer replaceRandNumGenerator(0)()
   467  
   468  	for _, tt := range []struct {
   469  		routes       []*xdsresource.Route
   470  		wantJSON     string
   471  		wantClusters map[string]bool
   472  	}{
   473  		{
   474  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   475  			wantJSON: `{"loadBalancingConfig":[{
   476      "xds_cluster_manager_experimental":{
   477        "children":{
   478          "cluster:test-cluster-1":{
   479            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   480          }
   481        }
   482      }}]}`,
   483  			wantClusters: map[string]bool{"cluster:test-cluster-1": true},
   484  		},
   485  		{
   486  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   487  				"cluster_1": {Weight: 75},
   488  				"cluster_2": {Weight: 25},
   489  			}}},
   490  			// This update contains the cluster from the previous update as
   491  			// well as this update, as the previous config selector still
   492  			// references the old cluster when the new one is pushed.
   493  			wantJSON: `{"loadBalancingConfig":[{
   494      "xds_cluster_manager_experimental":{
   495        "children":{
   496          "cluster:test-cluster-1":{
   497            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   498          },
   499          "cluster:cluster_1":{
   500            "childPolicy":[{"cds_experimental":{"cluster":"cluster_1"}}]
   501          },
   502          "cluster:cluster_2":{
   503            "childPolicy":[{"cds_experimental":{"cluster":"cluster_2"}}]
   504          }
   505        }
   506      }}]}`,
   507  			wantClusters: map[string]bool{"cluster:cluster_1": true, "cluster:cluster_2": true},
   508  		},
   509  		{
   510  			routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   511  				"cluster_1": {Weight: 75},
   512  				"cluster_2": {Weight: 25},
   513  			}}},
   514  			// With this redundant update, the old config selector has been
   515  			// stopped, so there are no more references to the first cluster.
   516  			// Only the second update's clusters should remain.
   517  			wantJSON: `{"loadBalancingConfig":[{
   518      "xds_cluster_manager_experimental":{
   519        "children":{
   520          "cluster:cluster_1":{
   521            "childPolicy":[{"cds_experimental":{"cluster":"cluster_1"}}]
   522          },
   523          "cluster:cluster_2":{
   524            "childPolicy":[{"cds_experimental":{"cluster":"cluster_2"}}]
   525          }
   526        }
   527      }}]}`,
   528  			wantClusters: map[string]bool{"cluster:cluster_1": true, "cluster:cluster_2": true},
   529  		},
   530  	} {
   531  		// Invoke the watchAPI callback with a good service update and wait for the
   532  		// UpdateState method to be called on the ClientConn.
   533  		xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   534  			VirtualHosts: []*xdsresource.VirtualHost{
   535  				{
   536  					Domains: []string{targetStr},
   537  					Routes:  tt.routes,
   538  				},
   539  			},
   540  		}, nil)
   541  
   542  		ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   543  		defer cancel()
   544  		gotState, err := tcc.stateCh.Receive(ctx)
   545  		if err != nil {
   546  			t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   547  		}
   548  		rState := gotState.(resolver.State)
   549  		if err := rState.ServiceConfig.Err; err != nil {
   550  			t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   551  		}
   552  
   553  		wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(tt.wantJSON)
   554  		if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   555  			t.Errorf("ClientConn.UpdateState received different service config")
   556  			t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   557  			t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   558  		}
   559  
   560  		cs := iresolver.GetConfigSelector(rState)
   561  		if cs == nil {
   562  			t.Error("received nil config selector")
   563  			continue
   564  		}
   565  
   566  		pickedClusters := make(map[string]bool)
   567  		// Odds of picking 75% cluster 100 times in a row: 1 in 3E-13.  And
   568  		// with the random number generator stubbed out, we can rely on this
   569  		// to be 100% reproducible.
   570  		for i := 0; i < 100; i++ {
   571  			res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   572  			if err != nil {
   573  				t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   574  			}
   575  			cluster := clustermanager.GetPickedClusterForTesting(res.Context)
   576  			pickedClusters[cluster] = true
   577  			res.OnCommitted()
   578  		}
   579  		if !reflect.DeepEqual(pickedClusters, tt.wantClusters) {
   580  			t.Errorf("Picked clusters: %v; want: %v", pickedClusters, tt.wantClusters)
   581  		}
   582  	}
   583  }
   584  
   585  // TestXDSResolverRequestHash tests a case where a resolver receives a RouteConfig update
   586  // with a HashPolicy specifying to generate a hash. The configSelector generated should
   587  // successfully generate a Hash.
   588  func (s) TestXDSResolverRequestHash(t *testing.T) {
   589  	oldRH := envconfig.XDSRingHash
   590  	envconfig.XDSRingHash = true
   591  	defer func() { envconfig.XDSRingHash = oldRH }()
   592  
   593  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   594  	defer xdsR.Close()
   595  	defer cancel()
   596  
   597  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   598  	defer cancel()
   599  	waitForWatchListener(ctx, t, xdsC, targetStr)
   600  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   601  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   602  	// Invoke watchAPI callback with a good service update (with hash policies
   603  	// specified) and wait for UpdateState method to be called on ClientConn.
   604  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   605  		VirtualHosts: []*xdsresource.VirtualHost{
   606  			{
   607  				Domains: []string{targetStr},
   608  				Routes: []*xdsresource.Route{{
   609  					Prefix: newStringP(""),
   610  					WeightedClusters: map[string]xdsresource.WeightedCluster{
   611  						"cluster_1": {Weight: 75},
   612  						"cluster_2": {Weight: 25},
   613  					},
   614  					HashPolicies: []*xdsresource.HashPolicy{{
   615  						HashPolicyType: xdsresource.HashPolicyTypeHeader,
   616  						HeaderName:     ":path",
   617  					}},
   618  				}},
   619  			},
   620  		},
   621  	}, nil)
   622  
   623  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
   624  	defer cancel()
   625  	gotState, err := tcc.stateCh.Receive(ctx)
   626  	if err != nil {
   627  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   628  	}
   629  	rState := gotState.(resolver.State)
   630  	cs := iresolver.GetConfigSelector(rState)
   631  	if cs == nil {
   632  		t.Error("received nil config selector")
   633  	}
   634  	// Selecting a config when there was a hash policy specified in the route
   635  	// that will be selected should put a request hash in the config's context.
   636  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: metadata.NewOutgoingContext(context.Background(), metadata.Pairs(":path", "/products"))})
   637  	if err != nil {
   638  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   639  	}
   640  	requestHashGot := ringhash.GetRequestHashForTesting(res.Context)
   641  	requestHashWant := xxhash.Sum64String("/products")
   642  	if requestHashGot != requestHashWant {
   643  		t.Fatalf("requestHashGot = %v, requestHashWant = %v", requestHashGot, requestHashWant)
   644  	}
   645  }
   646  
   647  // TestXDSResolverRemovedWithRPCs tests the case where a config selector sends
   648  // an empty update to the resolver after the resource is removed.
   649  func (s) TestXDSResolverRemovedWithRPCs(t *testing.T) {
   650  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   651  	defer cancel()
   652  	defer xdsR.Close()
   653  
   654  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   655  	defer cancel()
   656  	waitForWatchListener(ctx, t, xdsC, targetStr)
   657  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   658  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   659  
   660  	// Invoke the watchAPI callback with a good service update and wait for the
   661  	// UpdateState method to be called on the ClientConn.
   662  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   663  		VirtualHosts: []*xdsresource.VirtualHost{
   664  			{
   665  				Domains: []string{targetStr},
   666  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   667  			},
   668  		},
   669  	}, nil)
   670  
   671  	gotState, err := tcc.stateCh.Receive(ctx)
   672  	if err != nil {
   673  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   674  	}
   675  	rState := gotState.(resolver.State)
   676  	if err := rState.ServiceConfig.Err; err != nil {
   677  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   678  	}
   679  
   680  	// "Make an RPC" by invoking the config selector.
   681  	cs := iresolver.GetConfigSelector(rState)
   682  	if cs == nil {
   683  		t.Fatalf("received nil config selector")
   684  	}
   685  
   686  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   687  	if err != nil {
   688  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   689  	}
   690  
   691  	// Delete the resource
   692  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
   693  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   694  
   695  	if _, err = tcc.stateCh.Receive(ctx); err != nil {
   696  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   697  	}
   698  
   699  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   700  	// handle it correctly.
   701  	res.OnCommitted()
   702  }
   703  
   704  // TestXDSResolverRemovedResource tests for proper behavior after a resource is
   705  // removed.
   706  func (s) TestXDSResolverRemovedResource(t *testing.T) {
   707  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   708  	defer cancel()
   709  	defer xdsR.Close()
   710  
   711  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   712  	defer cancel()
   713  	waitForWatchListener(ctx, t, xdsC, targetStr)
   714  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   715  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   716  
   717  	// Invoke the watchAPI callback with a good service update and wait for the
   718  	// UpdateState method to be called on the ClientConn.
   719  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   720  		VirtualHosts: []*xdsresource.VirtualHost{
   721  			{
   722  				Domains: []string{targetStr},
   723  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   724  			},
   725  		},
   726  	}, nil)
   727  	wantJSON := `{"loadBalancingConfig":[{
   728      "xds_cluster_manager_experimental":{
   729        "children":{
   730          "cluster:test-cluster-1":{
   731            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   732          }
   733        }
   734      }}]}`
   735  	wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON)
   736  
   737  	gotState, err := tcc.stateCh.Receive(ctx)
   738  	if err != nil {
   739  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   740  	}
   741  	rState := gotState.(resolver.State)
   742  	if err := rState.ServiceConfig.Err; err != nil {
   743  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   744  	}
   745  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   746  		t.Errorf("ClientConn.UpdateState received different service config")
   747  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   748  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   749  	}
   750  
   751  	// "Make an RPC" by invoking the config selector.
   752  	cs := iresolver.GetConfigSelector(rState)
   753  	if cs == nil {
   754  		t.Fatalf("received nil config selector")
   755  	}
   756  
   757  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   758  	if err != nil {
   759  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   760  	}
   761  
   762  	// "Finish the RPC"; this could cause a panic if the resolver doesn't
   763  	// handle it correctly.
   764  	res.OnCommitted()
   765  
   766  	// Delete the resource.  The channel should receive a service config with the
   767  	// original cluster but with an erroring config selector.
   768  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
   769  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
   770  
   771  	if gotState, err = tcc.stateCh.Receive(ctx); err != nil {
   772  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   773  	}
   774  	rState = gotState.(resolver.State)
   775  	if err := rState.ServiceConfig.Err; err != nil {
   776  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   777  	}
   778  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   779  		t.Errorf("ClientConn.UpdateState received different service config")
   780  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   781  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   782  	}
   783  
   784  	// "Make another RPC" by invoking the config selector.
   785  	cs = iresolver.GetConfigSelector(rState)
   786  	if cs == nil {
   787  		t.Fatalf("received nil config selector")
   788  	}
   789  
   790  	res, err = cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   791  	if err == nil || status.Code(err) != codes.Unavailable {
   792  		t.Fatalf("Expected UNAVAILABLE error from cs.SelectConfig(_); got %v, %v", res, err)
   793  	}
   794  
   795  	// In the meantime, an empty ServiceConfig update should have been sent.
   796  	if gotState, err = tcc.stateCh.Receive(ctx); err != nil {
   797  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   798  	}
   799  	rState = gotState.(resolver.State)
   800  	if err := rState.ServiceConfig.Err; err != nil {
   801  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   802  	}
   803  	wantSCParsed = internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)("{}")
   804  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   805  		t.Errorf("ClientConn.UpdateState received different service config")
   806  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
   807  		t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))
   808  	}
   809  }
   810  
   811  func (s) TestXDSResolverWRR(t *testing.T) {
   812  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   813  	defer xdsR.Close()
   814  	defer cancel()
   815  
   816  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   817  	defer cancel()
   818  	waitForWatchListener(ctx, t, xdsC, targetStr)
   819  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   820  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   821  
   822  	defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
   823  	newWRR = testutils.NewTestWRR
   824  
   825  	// Invoke the watchAPI callback with a good service update and wait for the
   826  	// UpdateState method to be called on the ClientConn.
   827  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   828  		VirtualHosts: []*xdsresource.VirtualHost{
   829  			{
   830  				Domains: []string{targetStr},
   831  				Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{
   832  					"A": {Weight: 5},
   833  					"B": {Weight: 10},
   834  				}}},
   835  			},
   836  		},
   837  	}, nil)
   838  
   839  	gotState, err := tcc.stateCh.Receive(ctx)
   840  	if err != nil {
   841  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   842  	}
   843  	rState := gotState.(resolver.State)
   844  	if err := rState.ServiceConfig.Err; err != nil {
   845  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   846  	}
   847  
   848  	cs := iresolver.GetConfigSelector(rState)
   849  	if cs == nil {
   850  		t.Fatal("received nil config selector")
   851  	}
   852  
   853  	picks := map[string]int{}
   854  	for i := 0; i < 30; i++ {
   855  		res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
   856  		if err != nil {
   857  			t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
   858  		}
   859  		picks[clustermanager.GetPickedClusterForTesting(res.Context)]++
   860  		res.OnCommitted()
   861  	}
   862  	want := map[string]int{"cluster:A": 10, "cluster:B": 20}
   863  	if !reflect.DeepEqual(picks, want) {
   864  		t.Errorf("picked clusters = %v; want %v", picks, want)
   865  	}
   866  }
   867  
   868  func (s) TestXDSResolverMaxStreamDuration(t *testing.T) {
   869  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   870  	defer xdsR.Close()
   871  	defer cancel()
   872  
   873  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   874  	defer cancel()
   875  	waitForWatchListener(ctx, t, xdsC, targetStr)
   876  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList}, nil)
   877  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   878  
   879  	defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
   880  	newWRR = testutils.NewTestWRR
   881  
   882  	// Invoke the watchAPI callback with a good service update and wait for the
   883  	// UpdateState method to be called on the ClientConn.
   884  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   885  		VirtualHosts: []*xdsresource.VirtualHost{
   886  			{
   887  				Domains: []string{targetStr},
   888  				Routes: []*xdsresource.Route{{
   889  					Prefix:            newStringP("/foo"),
   890  					WeightedClusters:  map[string]xdsresource.WeightedCluster{"A": {Weight: 1}},
   891  					MaxStreamDuration: newDurationP(5 * time.Second),
   892  				}, {
   893  					Prefix:            newStringP("/bar"),
   894  					WeightedClusters:  map[string]xdsresource.WeightedCluster{"B": {Weight: 1}},
   895  					MaxStreamDuration: newDurationP(0),
   896  				}, {
   897  					Prefix:           newStringP(""),
   898  					WeightedClusters: map[string]xdsresource.WeightedCluster{"C": {Weight: 1}},
   899  				}},
   900  			},
   901  		},
   902  	}, nil)
   903  
   904  	gotState, err := tcc.stateCh.Receive(ctx)
   905  	if err != nil {
   906  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   907  	}
   908  	rState := gotState.(resolver.State)
   909  	if err := rState.ServiceConfig.Err; err != nil {
   910  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   911  	}
   912  
   913  	cs := iresolver.GetConfigSelector(rState)
   914  	if cs == nil {
   915  		t.Fatal("received nil config selector")
   916  	}
   917  
   918  	testCases := []struct {
   919  		name   string
   920  		method string
   921  		want   *time.Duration
   922  	}{{
   923  		name:   "RDS setting",
   924  		method: "/foo/method",
   925  		want:   newDurationP(5 * time.Second),
   926  	}, {
   927  		name:   "explicit zero in RDS; ignore LDS",
   928  		method: "/bar/method",
   929  		want:   nil,
   930  	}, {
   931  		name:   "no config in RDS; fallback to LDS",
   932  		method: "/baz/method",
   933  		want:   newDurationP(time.Second),
   934  	}}
   935  
   936  	for _, tc := range testCases {
   937  		t.Run(tc.name, func(t *testing.T) {
   938  			req := iresolver.RPCInfo{
   939  				Method:  tc.method,
   940  				Context: context.Background(),
   941  			}
   942  			res, err := cs.SelectConfig(req)
   943  			if err != nil {
   944  				t.Errorf("Unexpected error from cs.SelectConfig(%v): %v", req, err)
   945  				return
   946  			}
   947  			res.OnCommitted()
   948  			got := res.MethodConfig.Timeout
   949  			if !reflect.DeepEqual(got, tc.want) {
   950  				t.Errorf("For method %q: res.MethodConfig.Timeout = %v; want %v", tc.method, got, tc.want)
   951  			}
   952  		})
   953  	}
   954  }
   955  
   956  // TestXDSResolverDelayedOnCommitted tests that clusters remain in service
   957  // config if RPCs are in flight.
   958  func (s) TestXDSResolverDelayedOnCommitted(t *testing.T) {
   959  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
   960  	defer xdsR.Close()
   961  	defer cancel()
   962  
   963  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   964  	defer cancel()
   965  	waitForWatchListener(ctx, t, xdsC, targetStr)
   966  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
   967  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   968  
   969  	// Invoke the watchAPI callback with a good service update and wait for the
   970  	// UpdateState method to be called on the ClientConn.
   971  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   972  		VirtualHosts: []*xdsresource.VirtualHost{
   973  			{
   974  				Domains: []string{targetStr},
   975  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"test-cluster-1": {Weight: 1}}}},
   976  			},
   977  		},
   978  	}, nil)
   979  
   980  	gotState, err := tcc.stateCh.Receive(ctx)
   981  	if err != nil {
   982  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
   983  	}
   984  	rState := gotState.(resolver.State)
   985  	if err := rState.ServiceConfig.Err; err != nil {
   986  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
   987  	}
   988  
   989  	wantJSON := `{"loadBalancingConfig":[{
   990      "xds_cluster_manager_experimental":{
   991        "children":{
   992          "cluster:test-cluster-1":{
   993            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
   994          }
   995        }
   996      }}]}`
   997  	wantSCParsed := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON)
   998  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed.Config) {
   999  		t.Errorf("ClientConn.UpdateState received different service config")
  1000  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
  1001  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed.Config))
  1002  	}
  1003  
  1004  	cs := iresolver.GetConfigSelector(rState)
  1005  	if cs == nil {
  1006  		t.Fatal("received nil config selector")
  1007  	}
  1008  
  1009  	res, err := cs.SelectConfig(iresolver.RPCInfo{Context: context.Background()})
  1010  	if err != nil {
  1011  		t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
  1012  	}
  1013  	cluster := clustermanager.GetPickedClusterForTesting(res.Context)
  1014  	if cluster != "cluster:test-cluster-1" {
  1015  		t.Fatalf("")
  1016  	}
  1017  	// delay res.OnCommitted()
  1018  
  1019  	// Perform TWO updates to ensure the old config selector does not hold a
  1020  	// reference to test-cluster-1.
  1021  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1022  		VirtualHosts: []*xdsresource.VirtualHost{
  1023  			{
  1024  				Domains: []string{targetStr},
  1025  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1026  			},
  1027  		},
  1028  	}, nil)
  1029  	tcc.stateCh.Receive(ctx) // Ignore the first update.
  1030  
  1031  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1032  		VirtualHosts: []*xdsresource.VirtualHost{
  1033  			{
  1034  				Domains: []string{targetStr},
  1035  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1036  			},
  1037  		},
  1038  	}, nil)
  1039  
  1040  	gotState, err = tcc.stateCh.Receive(ctx)
  1041  	if err != nil {
  1042  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1043  	}
  1044  	rState = gotState.(resolver.State)
  1045  	if err := rState.ServiceConfig.Err; err != nil {
  1046  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1047  	}
  1048  	wantJSON2 := `{"loadBalancingConfig":[{
  1049      "xds_cluster_manager_experimental":{
  1050        "children":{
  1051          "cluster:test-cluster-1":{
  1052            "childPolicy":[{"cds_experimental":{"cluster":"test-cluster-1"}}]
  1053          },
  1054          "cluster:NEW":{
  1055            "childPolicy":[{"cds_experimental":{"cluster":"NEW"}}]
  1056          }
  1057        }
  1058      }}]}`
  1059  	wantSCParsed2 := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON2)
  1060  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed2.Config) {
  1061  		t.Errorf("ClientConn.UpdateState received different service config")
  1062  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
  1063  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed2.Config))
  1064  	}
  1065  
  1066  	// Invoke OnCommitted; should lead to a service config update that deletes
  1067  	// test-cluster-1.
  1068  	res.OnCommitted()
  1069  
  1070  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1071  		VirtualHosts: []*xdsresource.VirtualHost{
  1072  			{
  1073  				Domains: []string{targetStr},
  1074  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{"NEW": {Weight: 1}}}},
  1075  			},
  1076  		},
  1077  	}, nil)
  1078  	gotState, err = tcc.stateCh.Receive(ctx)
  1079  	if err != nil {
  1080  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1081  	}
  1082  	rState = gotState.(resolver.State)
  1083  	if err := rState.ServiceConfig.Err; err != nil {
  1084  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1085  	}
  1086  	wantJSON3 := `{"loadBalancingConfig":[{
  1087      "xds_cluster_manager_experimental":{
  1088        "children":{
  1089          "cluster:NEW":{
  1090            "childPolicy":[{"cds_experimental":{"cluster":"NEW"}}]
  1091          }
  1092        }
  1093      }}]}`
  1094  	wantSCParsed3 := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(wantJSON3)
  1095  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantSCParsed3.Config) {
  1096  		t.Errorf("ClientConn.UpdateState received different service config")
  1097  		t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
  1098  		t.Fatal("want: ", cmp.Diff(nil, wantSCParsed3.Config))
  1099  	}
  1100  }
  1101  
  1102  // TestXDSResolverUpdates tests the cases where the resolver gets a good update
  1103  // after an error, and an error after the good update.
  1104  func (s) TestXDSResolverGoodUpdateAfterError(t *testing.T) {
  1105  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1106  	defer xdsR.Close()
  1107  	defer cancel()
  1108  
  1109  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1110  	defer cancel()
  1111  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1112  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1113  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1114  
  1115  	// Invoke the watchAPI callback with a bad service update and wait for the
  1116  	// ReportError method to be called on the ClientConn.
  1117  	suErr := errors.New("bad serviceupdate")
  1118  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
  1119  
  1120  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr {
  1121  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr)
  1122  	}
  1123  
  1124  	// Invoke the watchAPI callback with a good service update and wait for the
  1125  	// UpdateState method to be called on the ClientConn.
  1126  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1127  		VirtualHosts: []*xdsresource.VirtualHost{
  1128  			{
  1129  				Domains: []string{targetStr},
  1130  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
  1131  			},
  1132  		},
  1133  	}, nil)
  1134  	gotState, err := tcc.stateCh.Receive(ctx)
  1135  	if err != nil {
  1136  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1137  	}
  1138  	rState := gotState.(resolver.State)
  1139  	if err := rState.ServiceConfig.Err; err != nil {
  1140  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1141  	}
  1142  
  1143  	// Invoke the watchAPI callback with a bad service update and wait for the
  1144  	// ReportError method to be called on the ClientConn.
  1145  	suErr2 := errors.New("bad serviceupdate 2")
  1146  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr2)
  1147  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != nil || gotErrVal != suErr2 {
  1148  		t.Fatalf("ClientConn.ReportError() received %v, want %v", gotErrVal, suErr2)
  1149  	}
  1150  }
  1151  
  1152  // TestXDSResolverResourceNotFoundError tests the cases where the resolver gets
  1153  // a ResourceNotFoundError. It should generate a service config picking
  1154  // weighted_target, but no child balancers.
  1155  func (s) TestXDSResolverResourceNotFoundError(t *testing.T) {
  1156  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1157  	defer xdsR.Close()
  1158  	defer cancel()
  1159  
  1160  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1161  	defer cancel()
  1162  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1163  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1164  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1165  
  1166  	// Invoke the watchAPI callback with a bad service update and wait for the
  1167  	// ReportError method to be called on the ClientConn.
  1168  	suErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "resource removed error")
  1169  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{}, suErr)
  1170  
  1171  	if gotErrVal, gotErr := tcc.errorCh.Receive(ctx); gotErr != context.DeadlineExceeded {
  1172  		t.Fatalf("ClientConn.ReportError() received %v, %v, want channel recv timeout", gotErrVal, gotErr)
  1173  	}
  1174  
  1175  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestTimeout)
  1176  	defer cancel()
  1177  	gotState, err := tcc.stateCh.Receive(ctx)
  1178  	if err != nil {
  1179  		t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1180  	}
  1181  	rState := gotState.(resolver.State)
  1182  	wantParsedConfig := internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)("{}")
  1183  	if !internal.EqualServiceConfigForTesting(rState.ServiceConfig.Config, wantParsedConfig.Config) {
  1184  		t.Error("ClientConn.UpdateState got wrong service config")
  1185  		t.Errorf("gotParsed: %s", cmp.Diff(nil, rState.ServiceConfig.Config))
  1186  		t.Errorf("wantParsed: %s", cmp.Diff(nil, wantParsedConfig.Config))
  1187  	}
  1188  	if err := rState.ServiceConfig.Err; err != nil {
  1189  		t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1190  	}
  1191  }
  1192  
  1193  // TestXDSResolverMultipleLDSUpdates tests the case where two LDS updates with
  1194  // the same RDS name to watch are received without an RDS in between. Those LDS
  1195  // updates shouldn't trigger service config update.
  1196  //
  1197  // This test case also makes sure the resolver doesn't panic.
  1198  func (s) TestXDSResolverMultipleLDSUpdates(t *testing.T) {
  1199  	xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1200  	defer xdsR.Close()
  1201  	defer cancel()
  1202  
  1203  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1204  	defer cancel()
  1205  	waitForWatchListener(ctx, t, xdsC, targetStr)
  1206  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1207  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1208  	defer replaceRandNumGenerator(0)()
  1209  
  1210  	// Send a new LDS update, with the same fields.
  1211  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, HTTPFilters: routerFilterList}, nil)
  1212  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1213  	defer cancel()
  1214  	// Should NOT trigger a state update.
  1215  	gotState, err := tcc.stateCh.Receive(ctx)
  1216  	if err == nil {
  1217  		t.Fatalf("ClientConn.UpdateState received %v, want timeout error", gotState)
  1218  	}
  1219  
  1220  	// Send a new LDS update, with the same RDS name, but different fields.
  1221  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList}, nil)
  1222  	ctx, cancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
  1223  	defer cancel()
  1224  	gotState, err = tcc.stateCh.Receive(ctx)
  1225  	if err == nil {
  1226  		t.Fatalf("ClientConn.UpdateState received %v, want timeout error", gotState)
  1227  	}
  1228  }
  1229  
  1230  type filterBuilder struct {
  1231  	httpfilter.Filter // embedded as we do not need to implement registry / parsing in this test.
  1232  	path              *[]string
  1233  }
  1234  
  1235  var _ httpfilter.ClientInterceptorBuilder = &filterBuilder{}
  1236  
  1237  func (fb *filterBuilder) BuildClientInterceptor(config, override httpfilter.FilterConfig) (iresolver.ClientInterceptor, error) {
  1238  	if config == nil {
  1239  		panic("unexpected missing config")
  1240  	}
  1241  	*fb.path = append(*fb.path, "build:"+config.(filterCfg).s)
  1242  	err := config.(filterCfg).newStreamErr
  1243  	if override != nil {
  1244  		*fb.path = append(*fb.path, "override:"+override.(filterCfg).s)
  1245  		err = override.(filterCfg).newStreamErr
  1246  	}
  1247  
  1248  	return &filterInterceptor{path: fb.path, s: config.(filterCfg).s, err: err}, nil
  1249  }
  1250  
  1251  type filterInterceptor struct {
  1252  	path *[]string
  1253  	s    string
  1254  	err  error
  1255  }
  1256  
  1257  func (fi *filterInterceptor) NewStream(ctx context.Context, ri iresolver.RPCInfo, done func(), newStream func(ctx context.Context, done func()) (iresolver.ClientStream, error)) (iresolver.ClientStream, error) {
  1258  	*fi.path = append(*fi.path, "newstream:"+fi.s)
  1259  	if fi.err != nil {
  1260  		return nil, fi.err
  1261  	}
  1262  	d := func() {
  1263  		*fi.path = append(*fi.path, "done:"+fi.s)
  1264  		done()
  1265  	}
  1266  	cs, err := newStream(ctx, d)
  1267  	if err != nil {
  1268  		return nil, err
  1269  	}
  1270  	return &clientStream{ClientStream: cs, path: fi.path, s: fi.s}, nil
  1271  }
  1272  
  1273  type clientStream struct {
  1274  	iresolver.ClientStream
  1275  	path *[]string
  1276  	s    string
  1277  }
  1278  
  1279  type filterCfg struct {
  1280  	httpfilter.FilterConfig
  1281  	s            string
  1282  	newStreamErr error
  1283  }
  1284  
  1285  func (s) TestXDSResolverHTTPFilters(t *testing.T) {
  1286  	var path []string
  1287  	testCases := []struct {
  1288  		name         string
  1289  		ldsFilters   []xdsresource.HTTPFilter
  1290  		vhOverrides  map[string]httpfilter.FilterConfig
  1291  		rtOverrides  map[string]httpfilter.FilterConfig
  1292  		clOverrides  map[string]httpfilter.FilterConfig
  1293  		rpcRes       map[string][][]string
  1294  		selectErr    string
  1295  		newStreamErr string
  1296  	}{
  1297  		{
  1298  			name: "no router filter",
  1299  			ldsFilters: []xdsresource.HTTPFilter{
  1300  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1301  			},
  1302  			rpcRes: map[string][][]string{
  1303  				"1": {
  1304  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1305  				},
  1306  			},
  1307  			selectErr: "no router filter present",
  1308  		},
  1309  		{
  1310  			name: "ignored after router filter",
  1311  			ldsFilters: []xdsresource.HTTPFilter{
  1312  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1313  				routerFilter,
  1314  				{Name: "foo2", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo2"}},
  1315  			},
  1316  			rpcRes: map[string][][]string{
  1317  				"1": {
  1318  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1319  				},
  1320  				"2": {
  1321  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1322  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1323  					{"build:foo1", "newstream:foo1", "done:foo1"},
  1324  				},
  1325  			},
  1326  		},
  1327  		{
  1328  			name: "NewStream error; ensure earlier interceptor Done is still called",
  1329  			ldsFilters: []xdsresource.HTTPFilter{
  1330  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1"}},
  1331  				{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1", newStreamErr: errors.New("bar newstream err")}},
  1332  				routerFilter,
  1333  			},
  1334  			rpcRes: map[string][][]string{
  1335  				"1": {
  1336  					{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewStream> */, "done:foo1"},
  1337  				},
  1338  				"2": {
  1339  					{"build:foo1", "build:bar1", "newstream:foo1", "newstream:bar1" /* <err in bar1 NewSteam> */, "done:foo1"},
  1340  				},
  1341  			},
  1342  			newStreamErr: "bar newstream err",
  1343  		},
  1344  		{
  1345  			name: "all overrides",
  1346  			ldsFilters: []xdsresource.HTTPFilter{
  1347  				{Name: "foo", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "foo1", newStreamErr: errors.New("this is overridden to nil")}},
  1348  				{Name: "bar", Filter: &filterBuilder{path: &path}, Config: filterCfg{s: "bar1"}},
  1349  				routerFilter,
  1350  			},
  1351  			vhOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo2"}, "bar": filterCfg{s: "bar2"}},
  1352  			rtOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo3"}, "bar": filterCfg{s: "bar3"}},
  1353  			clOverrides: map[string]httpfilter.FilterConfig{"foo": filterCfg{s: "foo4"}, "bar": filterCfg{s: "bar4"}},
  1354  			rpcRes: map[string][][]string{
  1355  				"1": {
  1356  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1357  					{"build:foo1", "override:foo2", "build:bar1", "override:bar2", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1358  				},
  1359  				"2": {
  1360  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1361  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1362  					{"build:foo1", "override:foo3", "build:bar1", "override:bar3", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1363  					{"build:foo1", "override:foo4", "build:bar1", "override:bar4", "newstream:foo1", "newstream:bar1", "done:bar1", "done:foo1"},
  1364  				},
  1365  			},
  1366  		},
  1367  	}
  1368  
  1369  	for i, tc := range testCases {
  1370  		t.Run(tc.name, func(t *testing.T) {
  1371  			xdsR, xdsC, tcc, cancel := testSetup(t, setupOpts{target: target})
  1372  			defer xdsR.Close()
  1373  			defer cancel()
  1374  
  1375  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
  1376  			defer cancel()
  1377  			waitForWatchListener(ctx, t, xdsC, targetStr)
  1378  
  1379  			xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{
  1380  				RouteConfigName: routeStr,
  1381  				HTTPFilters:     tc.ldsFilters,
  1382  			}, nil)
  1383  			if i == 0 {
  1384  				waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
  1385  			}
  1386  
  1387  			defer func(oldNewWRR func() wrr.WRR) { newWRR = oldNewWRR }(newWRR)
  1388  			newWRR = testutils.NewTestWRR
  1389  
  1390  			// Invoke the watchAPI callback with a good service update and wait for the
  1391  			// UpdateState method to be called on the ClientConn.
  1392  			xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
  1393  				VirtualHosts: []*xdsresource.VirtualHost{
  1394  					{
  1395  						Domains: []string{targetStr},
  1396  						Routes: []*xdsresource.Route{{
  1397  							Prefix: newStringP("1"), WeightedClusters: map[string]xdsresource.WeightedCluster{
  1398  								"A": {Weight: 1},
  1399  								"B": {Weight: 1},
  1400  							},
  1401  						}, {
  1402  							Prefix: newStringP("2"), WeightedClusters: map[string]xdsresource.WeightedCluster{
  1403  								"A": {Weight: 1},
  1404  								"B": {Weight: 1, HTTPFilterConfigOverride: tc.clOverrides},
  1405  							},
  1406  							HTTPFilterConfigOverride: tc.rtOverrides,
  1407  						}},
  1408  						HTTPFilterConfigOverride: tc.vhOverrides,
  1409  					},
  1410  				},
  1411  			}, nil)
  1412  
  1413  			gotState, err := tcc.stateCh.Receive(ctx)
  1414  			if err != nil {
  1415  				t.Fatalf("Error waiting for UpdateState to be called: %v", err)
  1416  			}
  1417  			rState := gotState.(resolver.State)
  1418  			if err := rState.ServiceConfig.Err; err != nil {
  1419  				t.Fatalf("ClientConn.UpdateState received error in service config: %v", rState.ServiceConfig.Err)
  1420  			}
  1421  
  1422  			cs := iresolver.GetConfigSelector(rState)
  1423  			if cs == nil {
  1424  				t.Fatal("received nil config selector")
  1425  			}
  1426  
  1427  			for method, wants := range tc.rpcRes {
  1428  				// Order of wants is non-deterministic.
  1429  				remainingWant := make([][]string, len(wants))
  1430  				copy(remainingWant, wants)
  1431  				for n := range wants {
  1432  					path = nil
  1433  
  1434  					res, err := cs.SelectConfig(iresolver.RPCInfo{Method: method, Context: context.Background()})
  1435  					if tc.selectErr != "" {
  1436  						if err == nil || !strings.Contains(err.Error(), tc.selectErr) {
  1437  							t.Errorf("SelectConfig(_) = _, %v; want _, Contains(%v)", err, tc.selectErr)
  1438  						}
  1439  						if err == nil {
  1440  							res.OnCommitted()
  1441  						}
  1442  						continue
  1443  					}
  1444  					if err != nil {
  1445  						t.Fatalf("Unexpected error from cs.SelectConfig(_): %v", err)
  1446  					}
  1447  					var doneFunc func()
  1448  					_, err = res.Interceptor.NewStream(context.Background(), iresolver.RPCInfo{}, func() {}, func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
  1449  						doneFunc = done
  1450  						return nil, nil
  1451  					})
  1452  					if tc.newStreamErr != "" {
  1453  						if err == nil || !strings.Contains(err.Error(), tc.newStreamErr) {
  1454  							t.Errorf("NewStream(...) = _, %v; want _, Contains(%v)", err, tc.newStreamErr)
  1455  						}
  1456  						if err == nil {
  1457  							res.OnCommitted()
  1458  							doneFunc()
  1459  						}
  1460  						continue
  1461  					}
  1462  					if err != nil {
  1463  						t.Fatalf("unexpected error from Interceptor.NewStream: %v", err)
  1464  
  1465  					}
  1466  					res.OnCommitted()
  1467  					doneFunc()
  1468  
  1469  					// Confirm the desired path is found in remainingWant, and remove it.
  1470  					pass := false
  1471  					for i := range remainingWant {
  1472  						if reflect.DeepEqual(path, remainingWant[i]) {
  1473  							remainingWant[i] = remainingWant[len(remainingWant)-1]
  1474  							remainingWant = remainingWant[:len(remainingWant)-1]
  1475  							pass = true
  1476  							break
  1477  						}
  1478  					}
  1479  					if !pass {
  1480  						t.Errorf("%q:%v - path:\n%v\nwant one of:\n%v", method, n, path, remainingWant)
  1481  					}
  1482  				}
  1483  			}
  1484  		})
  1485  	}
  1486  }
  1487  
  1488  func replaceRandNumGenerator(start int64) func() {
  1489  	nextInt := start
  1490  	xdsresource.RandInt63n = func(int64) (ret int64) {
  1491  		ret = nextInt
  1492  		nextInt++
  1493  		return
  1494  	}
  1495  	return func() {
  1496  		xdsresource.RandInt63n = grpcrand.Int63n
  1497  	}
  1498  }
  1499  
  1500  func newDurationP(d time.Duration) *time.Duration {
  1501  	return &d
  1502  }