google.golang.org/grpc@v1.62.1/balancer/conn_state_evaluator_test.go (about) 1 /* 2 * 3 * Copyright 2022 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 balancer 20 21 import ( 22 "testing" 23 24 "google.golang.org/grpc/connectivity" 25 "google.golang.org/grpc/internal/grpctest" 26 ) 27 28 type s struct { 29 grpctest.Tester 30 } 31 32 func Test(t *testing.T) { 33 grpctest.RunSubTests(t, s{}) 34 } 35 36 // TestRecordTransition_FirstStateChange tests the first call to 37 // RecordTransition where the `oldState` is usually set to `Shutdown` (a state 38 // that the ConnectivityStateEvaluator is set to ignore). 39 func (s) TestRecordTransition_FirstStateChange(t *testing.T) { 40 tests := []struct { 41 newState connectivity.State 42 wantState connectivity.State 43 }{ 44 { 45 newState: connectivity.Idle, 46 wantState: connectivity.Idle, 47 }, 48 { 49 newState: connectivity.Connecting, 50 wantState: connectivity.Connecting, 51 }, 52 { 53 newState: connectivity.Ready, 54 wantState: connectivity.Ready, 55 }, 56 { 57 newState: connectivity.TransientFailure, 58 wantState: connectivity.TransientFailure, 59 }, 60 { 61 newState: connectivity.Shutdown, 62 wantState: connectivity.TransientFailure, 63 }, 64 } 65 for _, test := range tests { 66 cse := &ConnectivityStateEvaluator{} 67 if gotState := cse.RecordTransition(connectivity.Shutdown, test.newState); gotState != test.wantState { 68 t.Fatalf("RecordTransition(%v, %v) = %v, want %v", connectivity.Shutdown, test.newState, gotState, test.wantState) 69 } 70 } 71 } 72 73 // TestRecordTransition_SameState tests the scenario where state transitions to 74 // the same state are recorded multiple times. 75 func (s) TestRecordTransition_SameState(t *testing.T) { 76 tests := []struct { 77 newState connectivity.State 78 wantState connectivity.State 79 }{ 80 { 81 newState: connectivity.Idle, 82 wantState: connectivity.Idle, 83 }, 84 { 85 newState: connectivity.Connecting, 86 wantState: connectivity.Connecting, 87 }, 88 { 89 newState: connectivity.Ready, 90 wantState: connectivity.Ready, 91 }, 92 { 93 newState: connectivity.TransientFailure, 94 wantState: connectivity.TransientFailure, 95 }, 96 { 97 newState: connectivity.Shutdown, 98 wantState: connectivity.TransientFailure, 99 }, 100 } 101 const numStateChanges = 5 102 for _, test := range tests { 103 cse := &ConnectivityStateEvaluator{} 104 var prevState, gotState connectivity.State 105 prevState = connectivity.Shutdown 106 for i := 0; i < numStateChanges; i++ { 107 gotState = cse.RecordTransition(prevState, test.newState) 108 prevState = test.newState 109 } 110 if gotState != test.wantState { 111 t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState) 112 } 113 } 114 } 115 116 // TestRecordTransition_SingleSubConn_DifferentStates tests some common 117 // connectivity state change scenarios, on a single subConn. 118 func (s) TestRecordTransition_SingleSubConn_DifferentStates(t *testing.T) { 119 tests := []struct { 120 name string 121 states []connectivity.State 122 wantState connectivity.State 123 }{ 124 { 125 name: "regular transition to ready", 126 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready}, 127 wantState: connectivity.Ready, 128 }, 129 { 130 name: "regular transition to transient failure", 131 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure}, 132 wantState: connectivity.TransientFailure, 133 }, 134 { 135 name: "regular transition to ready", 136 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Idle}, 137 wantState: connectivity.Idle, 138 }, 139 { 140 name: "transition from ready to transient failure", 141 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure}, 142 wantState: connectivity.TransientFailure, 143 }, 144 { 145 name: "transition from transient failure back to ready", 146 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Ready}, 147 wantState: connectivity.Ready, 148 }, 149 { 150 // This state transition is usually suppressed at the LB policy level, by 151 // not calling RecordTransition. 152 name: "transition from transient failure back to idle", 153 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Idle}, 154 wantState: connectivity.Idle, 155 }, 156 { 157 // This state transition is usually suppressed at the LB policy level, by 158 // not calling RecordTransition. 159 name: "transition from transient failure back to connecting", 160 states: []connectivity.State{connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure, connectivity.Connecting}, 161 wantState: connectivity.Connecting, 162 }, 163 } 164 165 for _, test := range tests { 166 t.Run(test.name, func(t *testing.T) { 167 cse := &ConnectivityStateEvaluator{} 168 var prevState, gotState connectivity.State 169 prevState = connectivity.Shutdown 170 for _, newState := range test.states { 171 gotState = cse.RecordTransition(prevState, newState) 172 prevState = newState 173 } 174 if gotState != test.wantState { 175 t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState) 176 } 177 }) 178 } 179 } 180 181 // TestRecordTransition_MultipleSubConns_DifferentStates tests state transitions 182 // among multiple subConns, and verifies that the connectivity state aggregation 183 // algorithm produces the expected aggregate connectivity state. 184 func (s) TestRecordTransition_MultipleSubConns_DifferentStates(t *testing.T) { 185 tests := []struct { 186 name string 187 // Each entry in this slice corresponds to the state changes happening on an 188 // individual subConn. 189 subConnStates [][]connectivity.State 190 wantState connectivity.State 191 }{ 192 { 193 name: "atleast one ready", 194 subConnStates: [][]connectivity.State{ 195 {connectivity.Idle, connectivity.Connecting, connectivity.Ready}, 196 {connectivity.Idle}, 197 {connectivity.Idle, connectivity.Connecting}, 198 {connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure}, 199 }, 200 wantState: connectivity.Ready, 201 }, 202 { 203 name: "atleast one connecting", 204 subConnStates: [][]connectivity.State{ 205 {connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Connecting}, 206 {connectivity.Idle}, 207 {connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure}, 208 }, 209 wantState: connectivity.Connecting, 210 }, 211 { 212 name: "atleast one idle", 213 subConnStates: [][]connectivity.State{ 214 {connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.Idle}, 215 {connectivity.Idle, connectivity.Connecting, connectivity.TransientFailure}, 216 }, 217 wantState: connectivity.Idle, 218 }, 219 { 220 name: "atleast one transient failure", 221 subConnStates: [][]connectivity.State{ 222 {connectivity.Idle, connectivity.Connecting, connectivity.Ready, connectivity.TransientFailure}, 223 {connectivity.TransientFailure}, 224 }, 225 wantState: connectivity.TransientFailure, 226 }, 227 } 228 229 for _, test := range tests { 230 t.Run(test.name, func(t *testing.T) { 231 cse := &ConnectivityStateEvaluator{} 232 var prevState, gotState connectivity.State 233 for _, scStates := range test.subConnStates { 234 prevState = connectivity.Shutdown 235 for _, newState := range scStates { 236 gotState = cse.RecordTransition(prevState, newState) 237 prevState = newState 238 } 239 } 240 if gotState != test.wantState { 241 t.Fatalf("RecordTransition() = %v, want %v", gotState, test.wantState) 242 } 243 }) 244 } 245 }