• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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