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

     1  /*
     2   *
     3   * Copyright 2020 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 testutils
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"testing"
    26  
    27  	"gitee.com/ks-custle/core-gm/grpc/balancer"
    28  	"gitee.com/ks-custle/core-gm/grpc/connectivity"
    29  	"gitee.com/ks-custle/core-gm/grpc/resolver"
    30  )
    31  
    32  // TestSubConnsCount is the number of TestSubConns initialized as part of
    33  // package init.
    34  const TestSubConnsCount = 16
    35  
    36  // testingLogger wraps the logging methods from testing.T.
    37  type testingLogger interface {
    38  	Log(args ...interface{})
    39  	Logf(format string, args ...interface{})
    40  }
    41  
    42  // TestSubConns contains a list of SubConns to be used in tests.
    43  var TestSubConns []*TestSubConn
    44  
    45  func init() {
    46  	for i := 0; i < TestSubConnsCount; i++ {
    47  		TestSubConns = append(TestSubConns, &TestSubConn{
    48  			id:        fmt.Sprintf("sc%d", i),
    49  			ConnectCh: make(chan struct{}, 1),
    50  		})
    51  	}
    52  }
    53  
    54  // TestSubConn implements the SubConn interface, to be used in tests.
    55  type TestSubConn struct {
    56  	id        string
    57  	ConnectCh chan struct{}
    58  }
    59  
    60  // UpdateAddresses is a no-op.
    61  func (tsc *TestSubConn) UpdateAddresses([]resolver.Address) {}
    62  
    63  // Connect is a no-op.
    64  func (tsc *TestSubConn) Connect() {
    65  	select {
    66  	case tsc.ConnectCh <- struct{}{}:
    67  	default:
    68  	}
    69  }
    70  
    71  // String implements stringer to print human friendly error message.
    72  func (tsc *TestSubConn) String() string {
    73  	return tsc.id
    74  }
    75  
    76  // TestClientConn is a mock balancer.ClientConn used in tests.
    77  type TestClientConn struct {
    78  	logger testingLogger
    79  
    80  	NewSubConnAddrsCh      chan []resolver.Address // the last 10 []Address to create subconn.
    81  	NewSubConnCh           chan balancer.SubConn   // the last 10 subconn created.
    82  	RemoveSubConnCh        chan balancer.SubConn   // the last 10 subconn removed.
    83  	UpdateAddressesAddrsCh chan []resolver.Address // last updated address via UpdateAddresses().
    84  
    85  	NewPickerCh  chan balancer.Picker            // the last picker updated.
    86  	NewStateCh   chan connectivity.State         // the last state.
    87  	ResolveNowCh chan resolver.ResolveNowOptions // the last ResolveNow().
    88  
    89  	subConnIdx int
    90  }
    91  
    92  // NewTestClientConn creates a TestClientConn.
    93  func NewTestClientConn(t *testing.T) *TestClientConn {
    94  	return &TestClientConn{
    95  		logger: t,
    96  
    97  		NewSubConnAddrsCh:      make(chan []resolver.Address, 10),
    98  		NewSubConnCh:           make(chan balancer.SubConn, 10),
    99  		RemoveSubConnCh:        make(chan balancer.SubConn, 10),
   100  		UpdateAddressesAddrsCh: make(chan []resolver.Address, 1),
   101  
   102  		NewPickerCh:  make(chan balancer.Picker, 1),
   103  		NewStateCh:   make(chan connectivity.State, 1),
   104  		ResolveNowCh: make(chan resolver.ResolveNowOptions, 1),
   105  	}
   106  }
   107  
   108  // NewSubConn creates a new SubConn.
   109  func (tcc *TestClientConn) NewSubConn(a []resolver.Address, o balancer.NewSubConnOptions) (balancer.SubConn, error) {
   110  	sc := TestSubConns[tcc.subConnIdx]
   111  	tcc.subConnIdx++
   112  
   113  	tcc.logger.Logf("testClientConn: NewSubConn(%v, %+v) => %s", a, o, sc)
   114  	select {
   115  	case tcc.NewSubConnAddrsCh <- a:
   116  	default:
   117  	}
   118  
   119  	select {
   120  	case tcc.NewSubConnCh <- sc:
   121  	default:
   122  	}
   123  
   124  	return sc, nil
   125  }
   126  
   127  // RemoveSubConn removes the SubConn.
   128  func (tcc *TestClientConn) RemoveSubConn(sc balancer.SubConn) {
   129  	tcc.logger.Logf("testClientConn: RemoveSubConn(%s)", sc)
   130  	select {
   131  	case tcc.RemoveSubConnCh <- sc:
   132  	default:
   133  	}
   134  }
   135  
   136  // UpdateAddresses updates the addresses on the SubConn.
   137  func (tcc *TestClientConn) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
   138  	tcc.logger.Logf("testClientConn: UpdateAddresses(%v, %+v)", sc, addrs)
   139  	select {
   140  	case tcc.UpdateAddressesAddrsCh <- addrs:
   141  	default:
   142  	}
   143  }
   144  
   145  // UpdateState updates connectivity state and picker.
   146  func (tcc *TestClientConn) UpdateState(bs balancer.State) {
   147  	tcc.logger.Logf("testClientConn: UpdateState(%v)", bs)
   148  	select {
   149  	case <-tcc.NewStateCh:
   150  	default:
   151  	}
   152  	tcc.NewStateCh <- bs.ConnectivityState
   153  
   154  	select {
   155  	case <-tcc.NewPickerCh:
   156  	default:
   157  	}
   158  	tcc.NewPickerCh <- bs.Picker
   159  }
   160  
   161  // ResolveNow panics.
   162  func (tcc *TestClientConn) ResolveNow(o resolver.ResolveNowOptions) {
   163  	select {
   164  	case <-tcc.ResolveNowCh:
   165  	default:
   166  	}
   167  	tcc.ResolveNowCh <- o
   168  }
   169  
   170  // Target panics.
   171  func (tcc *TestClientConn) Target() string {
   172  	panic("not implemented")
   173  }
   174  
   175  // WaitForErrPicker waits until an error picker is pushed to this ClientConn.
   176  // Returns error if the provided context expires or a non-error picker is pushed
   177  // to the ClientConn.
   178  func (tcc *TestClientConn) WaitForErrPicker(ctx context.Context) error {
   179  	select {
   180  	case <-ctx.Done():
   181  		return errors.New("timeout when waiting for an error picker")
   182  	case picker := <-tcc.NewPickerCh:
   183  		if _, perr := picker.Pick(balancer.PickInfo{}); perr == nil {
   184  			return fmt.Errorf("balancer returned a picker which is not an error picker")
   185  		}
   186  	}
   187  	return nil
   188  }
   189  
   190  // IsRoundRobin checks whether f's return value is roundrobin of elements from
   191  // want. But it doesn't check for the order. Note that want can contain
   192  // duplicate items, which makes it weight-round-robin.
   193  //
   194  // Step 1. the return values of f should form a permutation of all elements in
   195  // want, but not necessary in the same order. E.g. if want is {a,a,b}, the check
   196  // fails if f returns:
   197  //   - {a,a,a}: third a is returned before b
   198  //   - {a,b,b}: second b is returned before the second a
   199  //
   200  // If error is found in this step, the returned error contains only the first
   201  // iteration until where it goes wrong.
   202  //
   203  // Step 2. the return values of f should be repetitions of the same permutation.
   204  // E.g. if want is {a,a,b}, the check failes if f returns:
   205  //   - {a,b,a,b,a,a}: though it satisfies step 1, the second iteration is not
   206  //     repeating the first iteration.
   207  //
   208  // If error is found in this step, the returned error contains the first
   209  // iteration + the second iteration until where it goes wrong.
   210  func IsRoundRobin(want []balancer.SubConn, f func() balancer.SubConn) error {
   211  	wantSet := make(map[balancer.SubConn]int) // SubConn -> count, for weighted RR.
   212  	for _, sc := range want {
   213  		wantSet[sc]++
   214  	}
   215  
   216  	// The first iteration: makes sure f's return values form a permutation of
   217  	// elements in want.
   218  	//
   219  	// Also keep the returns values in a slice, so we can compare the order in
   220  	// the second iteration.
   221  	gotSliceFirstIteration := make([]balancer.SubConn, 0, len(want))
   222  	for range want {
   223  		got := f()
   224  		gotSliceFirstIteration = append(gotSliceFirstIteration, got)
   225  		wantSet[got]--
   226  		if wantSet[got] < 0 {
   227  			return fmt.Errorf("non-roundrobin want: %v, result: %v", want, gotSliceFirstIteration)
   228  		}
   229  	}
   230  
   231  	// The second iteration should repeat the first iteration.
   232  	var gotSliceSecondIteration []balancer.SubConn
   233  	for i := 0; i < 2; i++ {
   234  		for _, w := range gotSliceFirstIteration {
   235  			g := f()
   236  			gotSliceSecondIteration = append(gotSliceSecondIteration, g)
   237  			if w != g {
   238  				return fmt.Errorf("non-roundrobin, first iter: %v, second iter: %v", gotSliceFirstIteration, gotSliceSecondIteration)
   239  			}
   240  		}
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  // ErrTestConstPicker is error returned by test const picker.
   247  var ErrTestConstPicker = fmt.Errorf("const picker error")
   248  
   249  // TestConstPicker is a const picker for tests.
   250  type TestConstPicker struct {
   251  	Err error
   252  	SC  balancer.SubConn
   253  }
   254  
   255  // Pick returns the const SubConn or the error.
   256  func (tcp *TestConstPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
   257  	if tcp.Err != nil {
   258  		return balancer.PickResult{}, tcp.Err
   259  	}
   260  	return balancer.PickResult{SubConn: tcp.SC}, nil
   261  }