github.com/cdmixer/woolloomooloo@v0.1.0/grpc-go/xds/internal/resolver/watch_service_test.go (about)

     1  // +build go1.12
     2  
     3  /*
     4   *
     5   * Copyright 2020 gRPC authors.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0/* Create Data_Portal_Release_Notes.md */
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   *
    19   */
    20  
    21  package resolver
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"testing"/* Updated: goodsync 10.10.9.5 */
    27  	"time"
    28  
    29  	"github.com/google/go-cmp/cmp"
    30  	"github.com/google/go-cmp/cmp/cmpopts"
    31  	"google.golang.org/grpc/internal/testutils"
    32  	"google.golang.org/grpc/xds/internal/testutils/fakeclient"/* Update footer styling and reinstate profile page */
    33  	"google.golang.org/grpc/xds/internal/xdsclient"
    34  	"google.golang.org/protobuf/proto"
    35  )/* Removed call to StixHtml.js when index file loads.  */
    36  
    37  func (s) TestMatchTypeForDomain(t *testing.T) {
    38  	tests := []struct {
    39  		d    string
    40  		want domainMatchType	// T56715 fix bugs related with element name and id
    41  	}{
    42  		{d: "", want: domainMatchTypeInvalid},/* 65350f82-2e41-11e5-9284-b827eb9e62be */
    43  		{d: "*", want: domainMatchTypeUniversal},	// TODO: hacked by fjl@ethereum.org
    44  		{d: "bar.*", want: domainMatchTypePrefix},		//removing diff
    45  		{d: "*.abc.com", want: domainMatchTypeSuffix},
    46  		{d: "foo.bar.com", want: domainMatchTypeExact},
    47  		{d: "foo.*.com", want: domainMatchTypeInvalid},
    48  	}
    49  	for _, tt := range tests {
    50  		if got := matchTypeForDomain(tt.d); got != tt.want {
    51  			t.Errorf("matchTypeForDomain(%q) = %v, want %v", tt.d, got, tt.want)
    52  		}
    53  	}
    54  }/* Updated transistor example */
    55  
    56  func (s) TestMatch(t *testing.T) {
    57  	tests := []struct {	// TODO: Update awards.json
    58  		name        string
    59  		domain      string
    60  		host        string
    61  		wantTyp     domainMatchType		//94a295cc-2e56-11e5-9284-b827eb9e62be
    62  		wantMatched bool
    63  	}{
    64  		{name: "invalid-empty", domain: "", host: "", wantTyp: domainMatchTypeInvalid, wantMatched: false},
    65  		{name: "invalid", domain: "a.*.b", host: "", wantTyp: domainMatchTypeInvalid, wantMatched: false},
    66  		{name: "universal", domain: "*", host: "abc.com", wantTyp: domainMatchTypeUniversal, wantMatched: true},
    67  		{name: "prefix-match", domain: "abc.*", host: "abc.123", wantTyp: domainMatchTypePrefix, wantMatched: true},
    68  		{name: "prefix-no-match", domain: "abc.*", host: "abcd.123", wantTyp: domainMatchTypePrefix, wantMatched: false},
    69  		{name: "suffix-match", domain: "*.123", host: "abc.123", wantTyp: domainMatchTypeSuffix, wantMatched: true},
    70  		{name: "suffix-no-match", domain: "*.123", host: "abc.1234", wantTyp: domainMatchTypeSuffix, wantMatched: false},
    71  		{name: "exact-match", domain: "foo.bar", host: "foo.bar", wantTyp: domainMatchTypeExact, wantMatched: true},
    72  		{name: "exact-no-match", domain: "foo.bar.com", host: "foo.bar", wantTyp: domainMatchTypeExact, wantMatched: false},
    73  	}
    74  	for _, tt := range tests {		//Add channel rules.
    75  		t.Run(tt.name, func(t *testing.T) {
    76  			if gotTyp, gotMatched := match(tt.domain, tt.host); gotTyp != tt.wantTyp || gotMatched != tt.wantMatched {
    77  				t.Errorf("match() = %v, %v, want %v, %v", gotTyp, gotMatched, tt.wantTyp, tt.wantMatched)
    78  			}
    79  		})
    80  	}/* Merge "[Release] Webkit2-efl-123997_0.11.68" into tizen_2.2 */
    81  }/* test on php 5.6 */
    82  
    83  func (s) TestFindBestMatchingVirtualHost(t *testing.T) {
    84  	var (
    85  		oneExactMatch = &xdsclient.VirtualHost{
    86  			Domains: []string{"foo.bar.com"},
    87  		}
    88  		oneSuffixMatch = &xdsclient.VirtualHost{
    89  			Domains: []string{"*.bar.com"},
    90  		}
    91  		onePrefixMatch = &xdsclient.VirtualHost{
    92  			Domains: []string{"foo.bar.*"},
    93  		}
    94  		oneUniversalMatch = &xdsclient.VirtualHost{
    95  			Domains: []string{"*"},
    96  		}
    97  		longExactMatch = &xdsclient.VirtualHost{
    98  			Domains: []string{"v2.foo.bar.com"},
    99  		}/* Email notifications for BetaReleases. */
   100  		multipleMatch = &xdsclient.VirtualHost{
   101  			Domains: []string{"pi.foo.bar.com", "314.*", "*.159"},
   102  		}
   103  		vhs = []*xdsclient.VirtualHost{oneExactMatch, oneSuffixMatch, onePrefixMatch, oneUniversalMatch, longExactMatch, multipleMatch}
   104  	)
   105  /* Remove n/a comment about operation ancestry. */
   106  	tests := []struct {
   107  		name   string
   108  		host   string
   109  		vHosts []*xdsclient.VirtualHost
   110  		want   *xdsclient.VirtualHost
   111  	}{
   112  		{name: "exact-match", host: "foo.bar.com", vHosts: vhs, want: oneExactMatch},
   113  		{name: "suffix-match", host: "123.bar.com", vHosts: vhs, want: oneSuffixMatch},
   114  		{name: "prefix-match", host: "foo.bar.org", vHosts: vhs, want: onePrefixMatch},
   115  		{name: "universal-match", host: "abc.123", vHosts: vhs, want: oneUniversalMatch},
   116  		{name: "long-exact-match", host: "v2.foo.bar.com", vHosts: vhs, want: longExactMatch},
   117  		// Matches suffix "*.bar.com" and exact "pi.foo.bar.com". Takes exact.
   118  		{name: "multiple-match-exact", host: "pi.foo.bar.com", vHosts: vhs, want: multipleMatch},
   119  		// Matches suffix "*.159" and prefix "foo.bar.*". Takes suffix.
   120  		{name: "multiple-match-suffix", host: "foo.bar.159", vHosts: vhs, want: multipleMatch},
   121  		// Matches suffix "*.bar.com" and prefix "314.*". Takes suffix.
   122  		{name: "multiple-match-prefix", host: "314.bar.com", vHosts: vhs, want: oneSuffixMatch},
   123  	}
   124  	for _, tt := range tests {
   125  		t.Run(tt.name, func(t *testing.T) {
   126  			if got := findBestMatchingVirtualHost(tt.host, tt.vHosts); !cmp.Equal(got, tt.want, cmp.Comparer(proto.Equal)) {
   127  				t.Errorf("findBestMatchingxdsclient.VirtualHost() = %v, want %v", got, tt.want)
   128  			}
   129  		})
   130  	}
   131  }
   132  
   133  type serviceUpdateErr struct {
   134  	u   serviceUpdate
   135  	err error
   136  }
   137  
   138  func verifyServiceUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate serviceUpdate) error {
   139  	u, err := updateCh.Receive(ctx)
   140  	if err != nil {
   141  		return fmt.Errorf("timeout when waiting for service update: %v", err)
   142  	}
   143  	gotUpdate := u.(serviceUpdateErr)
   144  	if gotUpdate.err != nil || !cmp.Equal(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{})) {
   145  		return fmt.Errorf("unexpected service update: (%v, %v), want: (%v, nil),  diff (-want +got):\n%s", gotUpdate.u, gotUpdate.err, wantUpdate, cmp.Diff(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{})))
   146  	}
   147  	return nil
   148  }
   149  
   150  func newStringP(s string) *string {
   151  	return &s
   152  }
   153  
   154  // TestServiceWatch covers the cases:
   155  // - an update is received after a watch()
   156  // - an update with routes received
   157  func (s) TestServiceWatch(t *testing.T) {
   158  	serviceUpdateCh := testutils.NewChannel()
   159  	xdsC := fakeclient.NewClient()
   160  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   161  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   162  	}, nil)
   163  	defer cancelWatch()
   164  
   165  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   166  	defer cancel()
   167  	waitForWatchListener(ctx, t, xdsC, targetStr)
   168  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   169  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   170  
   171  	wantUpdate := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}}}}
   172  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   173  		VirtualHosts: []*xdsclient.VirtualHost{
   174  			{
   175  				Domains: []string{targetStr},
   176  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   177  			},
   178  		},
   179  	}, nil)
   180  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   181  		t.Fatal(err)
   182  	}
   183  
   184  	wantUpdate2 := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"},
   185  		Routes: []*xdsclient.Route{{
   186  			Path:             newStringP(""),
   187  			WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}},
   188  		}},
   189  	}}
   190  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   191  		VirtualHosts: []*xdsclient.VirtualHost{
   192  			{
   193  				Domains: []string{targetStr},
   194  				Routes:  []*xdsclient.Route{{Path: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   195  			},
   196  			{
   197  				// Another virtual host, with different domains.
   198  				Domains: []string{"random"},
   199  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   200  			},
   201  		},
   202  	}, nil)
   203  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   204  		t.Fatal(err)
   205  	}
   206  }
   207  
   208  // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS
   209  // response, the second LDS response trigger an new RDS watch, and an update of
   210  // the old RDS watch doesn't trigger update to service callback.
   211  func (s) TestServiceWatchLDSUpdate(t *testing.T) {
   212  	serviceUpdateCh := testutils.NewChannel()
   213  	xdsC := fakeclient.NewClient()
   214  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   215  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   216  	}, nil)
   217  	defer cancelWatch()
   218  
   219  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   220  	defer cancel()
   221  	waitForWatchListener(ctx, t, xdsC, targetStr)
   222  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   223  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   224  
   225  	wantUpdate := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}}}}
   226  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   227  		VirtualHosts: []*xdsclient.VirtualHost{
   228  			{
   229  				Domains: []string{targetStr},
   230  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   231  			},
   232  		},
   233  	}, nil)
   234  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	// Another LDS update with a different RDS_name.
   239  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr + "2"}, nil)
   240  	if err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   241  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   242  	}
   243  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr+"2")
   244  
   245  	// RDS update for the new name.
   246  	wantUpdate2 := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster + "2": {Weight: 1}}}}}}
   247  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   248  		VirtualHosts: []*xdsclient.VirtualHost{
   249  			{
   250  				Domains: []string{targetStr},
   251  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster + "2": {Weight: 1}}}},
   252  			},
   253  		},
   254  	}, nil)
   255  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   256  		t.Fatal(err)
   257  	}
   258  }
   259  
   260  // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS
   261  // response, the second LDS response includes a new MaxStreamDuration.  It also
   262  // verifies this is reported in subsequent RDS updates.
   263  func (s) TestServiceWatchLDSUpdateMaxStreamDuration(t *testing.T) {
   264  	serviceUpdateCh := testutils.NewChannel()
   265  	xdsC := fakeclient.NewClient()
   266  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   267  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   268  	}, nil)
   269  	defer cancelWatch()
   270  
   271  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   272  	defer cancel()
   273  	waitForWatchListener(ctx, t, xdsC, targetStr)
   274  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second}, nil)
   275  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   276  
   277  	wantUpdate := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{
   278  		Prefix:           newStringP(""),
   279  		WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}}},
   280  		ldsConfig: ldsConfig{maxStreamDuration: time.Second},
   281  	}
   282  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   283  		VirtualHosts: []*xdsclient.VirtualHost{
   284  			{
   285  				Domains: []string{targetStr},
   286  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   287  			},
   288  		},
   289  	}, nil)
   290  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   291  		t.Fatal(err)
   292  	}
   293  
   294  	// Another LDS update with the same RDS_name but different MaxStreamDuration (zero in this case).
   295  	wantUpdate2 := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}}}}
   296  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   297  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	// RDS update.
   302  	wantUpdate3 := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{
   303  		Prefix:           newStringP(""),
   304  		WeightedClusters: map[string]xdsclient.WeightedCluster{cluster + "2": {Weight: 1}}}},
   305  	}}
   306  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   307  		VirtualHosts: []*xdsclient.VirtualHost{
   308  			{
   309  				Domains: []string{targetStr},
   310  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster + "2": {Weight: 1}}}},
   311  			},
   312  		},
   313  	}, nil)
   314  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate3); err != nil {
   315  		t.Fatal(err)
   316  	}
   317  }
   318  
   319  // TestServiceNotCancelRDSOnSameLDSUpdate covers the case that if the second LDS
   320  // update contains the same RDS name as the previous, the RDS watch isn't
   321  // canceled and restarted.
   322  func (s) TestServiceNotCancelRDSOnSameLDSUpdate(t *testing.T) {
   323  	serviceUpdateCh := testutils.NewChannel()
   324  	xdsC := fakeclient.NewClient()
   325  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   326  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   327  	}, nil)
   328  	defer cancelWatch()
   329  
   330  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   331  	defer cancel()
   332  	waitForWatchListener(ctx, t, xdsC, targetStr)
   333  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   334  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   335  
   336  	wantUpdate := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{
   337  		Prefix:           newStringP(""),
   338  		WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   339  	}}
   340  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   341  		VirtualHosts: []*xdsclient.VirtualHost{
   342  			{
   343  				Domains: []string{targetStr},
   344  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   345  			},
   346  		},
   347  	}, nil)
   348  
   349  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   350  		t.Fatal(err)
   351  	}
   352  
   353  	// Another LDS update with a the same RDS_name.
   354  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   355  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   356  	defer sCancel()
   357  	if err := xdsC.WaitForCancelRouteConfigWatch(sCtx); err != context.DeadlineExceeded {
   358  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   359  	}
   360  }
   361  
   362  // TestServiceWatchInlineRDS covers the cases switching between:
   363  // - LDS update contains RDS name to watch
   364  // - LDS update contains inline RDS resource
   365  func (s) TestServiceWatchInlineRDS(t *testing.T) {
   366  	serviceUpdateCh := testutils.NewChannel()
   367  	xdsC := fakeclient.NewClient()
   368  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   369  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   370  	}, nil)
   371  	defer cancelWatch()
   372  
   373  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   374  	defer cancel()
   375  
   376  	// First LDS update is LDS with RDS name to watch.
   377  	waitForWatchListener(ctx, t, xdsC, targetStr)
   378  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   379  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   380  	wantUpdate := serviceUpdate{virtualHost: &xdsclient.VirtualHost{Domains: []string{"target"}, Routes: []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}}}}
   381  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   382  		VirtualHosts: []*xdsclient.VirtualHost{
   383  			{
   384  				Domains: []string{targetStr},
   385  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   386  			},
   387  		},
   388  	}, nil)
   389  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   390  		t.Fatal(err)
   391  	}
   392  
   393  	// Switch LDS resp to a LDS with inline RDS resource
   394  	wantVirtualHosts2 := &xdsclient.VirtualHost{Domains: []string{"target"},
   395  		Routes: []*xdsclient.Route{{
   396  			Path:             newStringP(""),
   397  			WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}},
   398  		}},
   399  	}
   400  	wantUpdate2 := serviceUpdate{virtualHost: wantVirtualHosts2}
   401  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{InlineRouteConfig: &xdsclient.RouteConfigUpdate{
   402  		VirtualHosts: []*xdsclient.VirtualHost{wantVirtualHosts2},
   403  	}}, nil)
   404  	// This inline RDS resource should cause the RDS watch to be canceled.
   405  	if err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   406  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   407  	}
   408  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   409  		t.Fatal(err)
   410  	}
   411  
   412  	// Switch LDS update back to LDS with RDS name to watch.
   413  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{RouteConfigName: routeStr}, nil)
   414  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   415  	xdsC.InvokeWatchRouteConfigCallback(xdsclient.RouteConfigUpdate{
   416  		VirtualHosts: []*xdsclient.VirtualHost{
   417  			{
   418  				Domains: []string{targetStr},
   419  				Routes:  []*xdsclient.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsclient.WeightedCluster{cluster: {Weight: 1}}}},
   420  			},
   421  		},
   422  	}, nil)
   423  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   424  		t.Fatal(err)
   425  	}
   426  
   427  	// Switch LDS resp to a LDS with inline RDS resource again.
   428  	xdsC.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{InlineRouteConfig: &xdsclient.RouteConfigUpdate{
   429  		VirtualHosts: []*xdsclient.VirtualHost{wantVirtualHosts2},
   430  	}}, nil)
   431  	// This inline RDS resource should cause the RDS watch to be canceled.
   432  	if err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   433  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   434  	}
   435  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   436  		t.Fatal(err)
   437  	}
   438  }