1 /* 2 * Copyright 2017 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.fail; 21 import static org.mockito.Mockito.mock; 22 23 import io.grpc.LoadBalancer.CreateSubchannelArgs; 24 import io.grpc.LoadBalancer.PickResult; 25 import io.grpc.LoadBalancer.ResolvedAddresses; 26 import io.grpc.LoadBalancer.Subchannel; 27 import java.net.SocketAddress; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.List; 31 import java.util.concurrent.atomic.AtomicReference; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 import org.junit.runners.JUnit4; 35 36 /** Unit tests for the inner classes in {@link LoadBalancer}. */ 37 @RunWith(JUnit4.class) 38 public class LoadBalancerTest { 39 private final Subchannel subchannel = mock(Subchannel.class); 40 private final Subchannel subchannel2 = mock(Subchannel.class); 41 private final ClientStreamTracer.Factory tracerFactory = mock(ClientStreamTracer.Factory.class); 42 private final Status status = Status.UNAVAILABLE.withDescription("for test"); 43 private final Status status2 = Status.UNAVAILABLE.withDescription("for test 2"); 44 private final EquivalentAddressGroup eag = new EquivalentAddressGroup(new SocketAddress() {}); 45 private final Attributes attrs = Attributes.newBuilder() 46 .set(Attributes.Key.create("trash"), new Object()) 47 .build(); 48 49 @Test pickResult_withSubchannel()50 public void pickResult_withSubchannel() { 51 PickResult result = PickResult.withSubchannel(subchannel); 52 assertThat(result.getSubchannel()).isSameInstanceAs(subchannel); 53 assertThat(result.getStatus()).isSameInstanceAs(Status.OK); 54 assertThat(result.getStreamTracerFactory()).isNull(); 55 assertThat(result.isDrop()).isFalse(); 56 } 57 58 @Test pickResult_withSubchannelAndTracer()59 public void pickResult_withSubchannelAndTracer() { 60 PickResult result = PickResult.withSubchannel(subchannel, tracerFactory); 61 assertThat(result.getSubchannel()).isSameInstanceAs(subchannel); 62 assertThat(result.getStatus()).isSameInstanceAs(Status.OK); 63 assertThat(result.getStreamTracerFactory()).isSameInstanceAs(tracerFactory); 64 assertThat(result.isDrop()).isFalse(); 65 } 66 67 @Test pickResult_withNoResult()68 public void pickResult_withNoResult() { 69 PickResult result = PickResult.withNoResult(); 70 assertThat(result.getSubchannel()).isNull(); 71 assertThat(result.getStatus()).isSameInstanceAs(Status.OK); 72 assertThat(result.getStreamTracerFactory()).isNull(); 73 assertThat(result.isDrop()).isFalse(); 74 } 75 76 @Test pickResult_withError()77 public void pickResult_withError() { 78 PickResult result = PickResult.withError(status); 79 assertThat(result.getSubchannel()).isNull(); 80 assertThat(result.getStatus()).isSameInstanceAs(status); 81 assertThat(result.getStreamTracerFactory()).isNull(); 82 assertThat(result.isDrop()).isFalse(); 83 } 84 85 @Test pickResult_withDrop()86 public void pickResult_withDrop() { 87 PickResult result = PickResult.withDrop(status); 88 assertThat(result.getSubchannel()).isNull(); 89 assertThat(result.getStatus()).isSameInstanceAs(status); 90 assertThat(result.getStreamTracerFactory()).isNull(); 91 assertThat(result.isDrop()).isTrue(); 92 } 93 94 @Test pickResult_equals()95 public void pickResult_equals() { 96 PickResult sc1 = PickResult.withSubchannel(subchannel); 97 PickResult sc2 = PickResult.withSubchannel(subchannel); 98 PickResult sc3 = PickResult.withSubchannel(subchannel, tracerFactory); 99 PickResult sc4 = PickResult.withSubchannel(subchannel2); 100 PickResult nr = PickResult.withNoResult(); 101 PickResult error1 = PickResult.withError(status); 102 PickResult error2 = PickResult.withError(status2); 103 PickResult error3 = PickResult.withError(status2); 104 PickResult drop1 = PickResult.withDrop(status); 105 PickResult drop2 = PickResult.withDrop(status); 106 PickResult drop3 = PickResult.withDrop(status2); 107 108 assertThat(sc1).isNotEqualTo(nr); 109 assertThat(sc1).isNotEqualTo(error1); 110 assertThat(sc1).isNotEqualTo(drop1); 111 assertThat(sc1).isEqualTo(sc2); 112 assertThat(sc1).isNotEqualTo(sc3); 113 assertThat(sc1).isNotEqualTo(sc4); 114 115 assertThat(error1).isNotEqualTo(error2); 116 assertThat(error2).isEqualTo(error3); 117 118 assertThat(drop1).isEqualTo(drop2); 119 assertThat(drop1).isNotEqualTo(drop3); 120 121 assertThat(error1.getStatus()).isEqualTo(drop1.getStatus()); 122 assertThat(error1).isNotEqualTo(drop1); 123 } 124 125 @Test helper_createSubchannelList_throws()126 public void helper_createSubchannelList_throws() { 127 try { 128 new NoopHelper().createSubchannel(CreateSubchannelArgs.newBuilder() 129 .setAddresses(eag) 130 .setAttributes(attrs) 131 .build()); 132 fail("Should throw"); 133 } catch (UnsupportedOperationException e) { 134 // expected 135 } 136 } 137 138 @Test subchannel_getAddresses_delegates()139 public void subchannel_getAddresses_delegates() { 140 class OverrideGetAllAddresses extends EmptySubchannel { 141 boolean ran; 142 143 @Override public List<EquivalentAddressGroup> getAllAddresses() { 144 ran = true; 145 return Arrays.asList(eag); 146 } 147 } 148 149 OverrideGetAllAddresses subchannel = new OverrideGetAllAddresses(); 150 assertThat(subchannel.getAddresses()).isEqualTo(eag); 151 assertThat(subchannel.ran).isTrue(); 152 } 153 154 @Test(expected = IllegalStateException.class) subchannel_getAddresses_throwsOnTwoAddrs()155 public void subchannel_getAddresses_throwsOnTwoAddrs() { 156 new EmptySubchannel() { 157 boolean ran; 158 159 @Override public List<EquivalentAddressGroup> getAllAddresses() { 160 ran = true; 161 // Doubling up eag is technically a bad idea, but nothing here cares 162 return Arrays.asList(eag, eag); 163 } 164 }.getAddresses(); 165 } 166 167 @Test createSubchannelArgs_option_keyOps()168 public void createSubchannelArgs_option_keyOps() { 169 CreateSubchannelArgs.Key<String> testKey = CreateSubchannelArgs.Key.create("test-key"); 170 String testValue = "test-value"; 171 CreateSubchannelArgs.Key<String> testWithDefaultKey = CreateSubchannelArgs.Key 172 .createWithDefault("test-key", testValue); 173 CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() 174 .setAddresses(eag) 175 .setAttributes(attrs) 176 .build(); 177 assertThat(args.getOption(testKey)).isNull(); 178 assertThat(args.getOption(testWithDefaultKey)).isSameInstanceAs(testValue); 179 } 180 181 @Test createSubchannelArgs_option_addGet()182 public void createSubchannelArgs_option_addGet() { 183 String testValue = "test-value"; 184 CreateSubchannelArgs.Key<String> testKey = CreateSubchannelArgs.Key.create("test-key"); 185 CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() 186 .setAddresses(eag) 187 .setAttributes(attrs) 188 .addOption(testKey, testValue) 189 .build(); 190 assertThat(args.getOption(testKey)).isEqualTo(testValue); 191 } 192 193 @Test createSubchannelArgs_option_lastOneWins()194 public void createSubchannelArgs_option_lastOneWins() { 195 String testValue1 = "test-value-1"; 196 String testValue2 = "test-value-2"; 197 CreateSubchannelArgs.Key<String> testKey = CreateSubchannelArgs.Key.create("test-key"); 198 CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() 199 .setAddresses(eag) 200 .setAttributes(attrs) 201 .addOption(testKey, testValue1) 202 .addOption(testKey, testValue2) 203 .build(); 204 assertThat(args.getOption(testKey)).isEqualTo(testValue2); 205 } 206 207 @Test createSubchannelArgs_build()208 public void createSubchannelArgs_build() { 209 CreateSubchannelArgs.Key<Object> testKey = CreateSubchannelArgs.Key.create("test-key"); 210 Object testValue = new Object(); 211 CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() 212 .setAddresses(eag) 213 .setAttributes(attrs) 214 .addOption(testKey, testValue) 215 .build(); 216 CreateSubchannelArgs rebuildedArgs = args.toBuilder().build(); 217 assertThat(rebuildedArgs.getAddresses()).containsExactly(eag); 218 assertThat(rebuildedArgs.getAttributes()).isSameInstanceAs(attrs); 219 assertThat(rebuildedArgs.getOption(testKey)).isSameInstanceAs(testValue); 220 } 221 222 @Test createSubchannelArgs_toString()223 public void createSubchannelArgs_toString() { 224 CreateSubchannelArgs.Key<String> testKey = CreateSubchannelArgs.Key.create("test-key"); 225 CreateSubchannelArgs args = CreateSubchannelArgs.newBuilder() 226 .setAddresses(eag) 227 .setAttributes(attrs) 228 .addOption(testKey, "test-value") 229 .build(); 230 String str = args.toString(); 231 assertThat(str).contains("addrs="); 232 assertThat(str).contains("attrs="); 233 assertThat(str).contains("customOptions="); 234 } 235 236 @Deprecated 237 @Test handleResolvedAddresses_delegatesToAcceptResolvedAddresses()238 public void handleResolvedAddresses_delegatesToAcceptResolvedAddresses() { 239 final AtomicReference<ResolvedAddresses> resultCapture = new AtomicReference<>(); 240 241 LoadBalancer balancer = new LoadBalancer() { 242 @Override 243 public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) { 244 resultCapture.set(resolvedAddresses); 245 return true; 246 } 247 248 @Override 249 public void handleNameResolutionError(Status error) { 250 } 251 252 @Override 253 public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo state) { 254 } 255 256 @Override 257 public void shutdown() { 258 } 259 }; 260 261 List<EquivalentAddressGroup> servers = Arrays.asList( 262 new EquivalentAddressGroup(new SocketAddress(){}), 263 new EquivalentAddressGroup(new SocketAddress(){})); 264 ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) 265 .setAttributes(attrs).build(); 266 balancer.handleResolvedAddresses(addresses); 267 assertThat(resultCapture.get()).isEqualTo( 268 ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attrs).build()); 269 } 270 271 @Deprecated 272 @Test acceptResolvedAddresses_delegatesToHandleResolvedAddressGroups()273 public void acceptResolvedAddresses_delegatesToHandleResolvedAddressGroups() { 274 final AtomicReference<ResolvedAddresses> addressesCapture = new AtomicReference<>(); 275 276 LoadBalancer balancer = new LoadBalancer() { 277 @Override 278 public void handleResolvedAddresses(ResolvedAddresses addresses) { 279 addressesCapture.set(addresses); 280 } 281 282 @Override 283 public void handleNameResolutionError(Status error) { 284 } 285 286 @Override 287 public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo state) { 288 } 289 290 @Override 291 public void shutdown() { 292 } 293 }; 294 295 List<EquivalentAddressGroup> servers = Arrays.asList( 296 new EquivalentAddressGroup(new SocketAddress(){}), 297 new EquivalentAddressGroup(new SocketAddress(){})); 298 ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) 299 .setAttributes(attrs).build(); 300 balancer.handleResolvedAddresses(addresses); 301 assertThat(addressesCapture.get().getAddresses()).isEqualTo(servers); 302 assertThat(addressesCapture.get().getAttributes()).isEqualTo(attrs); 303 } 304 305 @Deprecated 306 @Test acceptResolvedAddresses_noInfiniteLoop()307 public void acceptResolvedAddresses_noInfiniteLoop() { 308 final List<ResolvedAddresses> addressesCapture = new ArrayList<>(); 309 310 LoadBalancer balancer = new LoadBalancer() { 311 @Override 312 public void handleResolvedAddresses(ResolvedAddresses addresses) { 313 addressesCapture.add(addresses); 314 super.handleResolvedAddresses(addresses); 315 } 316 317 @Override 318 public void handleNameResolutionError(Status error) { 319 } 320 321 @Override 322 public void shutdown() { 323 } 324 }; 325 326 List<EquivalentAddressGroup> servers = Arrays.asList( 327 new EquivalentAddressGroup(new SocketAddress(){}), 328 new EquivalentAddressGroup(new SocketAddress(){})); 329 ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers) 330 .setAttributes(attrs).build(); 331 balancer.handleResolvedAddresses(addresses); 332 assertThat(addressesCapture).hasSize(1); 333 assertThat(addressesCapture.get(0).getAddresses()).isEqualTo(servers); 334 assertThat(addressesCapture.get(0).getAttributes()).isEqualTo(attrs); 335 } 336 337 private static class NoopHelper extends LoadBalancer.Helper { 338 @Override createOobChannel(EquivalentAddressGroup eag, String authority)339 public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) { 340 return null; 341 } 342 343 @Override updateBalancingState( ConnectivityState newState, LoadBalancer.SubchannelPicker newPicker)344 public void updateBalancingState( 345 ConnectivityState newState, LoadBalancer.SubchannelPicker newPicker) {} 346 getSynchronizationContext()347 @Override public SynchronizationContext getSynchronizationContext() { 348 return null; 349 } 350 getAuthority()351 @Override public String getAuthority() { 352 return null; 353 } 354 } 355 356 private static class EmptySubchannel extends LoadBalancer.Subchannel { shutdown()357 @Override public void shutdown() {} 358 requestConnection()359 @Override public void requestConnection() {} 360 getAttributes()361 @Override public Attributes getAttributes() { 362 return null; 363 } 364 } 365 } 366