|
26 | 26 | import static org.mockito.AdditionalAnswers.delegatesTo; |
27 | 27 | import static org.mockito.ArgumentMatchers.any; |
28 | 28 | import static org.mockito.ArgumentMatchers.eq; |
| 29 | +import static org.mockito.ArgumentMatchers.isA; |
29 | 30 | import static org.mockito.ArgumentMatchers.same; |
| 31 | +import static org.mockito.Mockito.atMostOnce; |
30 | 32 | import static org.mockito.Mockito.mock; |
31 | 33 | import static org.mockito.Mockito.never; |
32 | 34 | import static org.mockito.Mockito.times; |
@@ -284,6 +286,35 @@ public void delayedTransportHoldsOffIdleness() throws Exception { |
284 | 286 | verify(mockLoadBalancer).shutdown(); |
285 | 287 | } |
286 | 288 |
|
| 289 | + @Test |
| 290 | + public void pendingCallExitsIdleAfterEnter() throws Exception { |
| 291 | + // Create a pending call without starting it. |
| 292 | + channel.newCall(method, CallOptions.DEFAULT); |
| 293 | + |
| 294 | + channel.enterIdle(); |
| 295 | + |
| 296 | + // Just the existence of a non-started, pending call means the channel cannot stay |
| 297 | + // in idle mode because the expectation is that the pending call will also need to |
| 298 | + // be handled. |
| 299 | + verify(mockNameResolver, times(2)).start(any(NameResolver.Listener2.class)); |
| 300 | + } |
| 301 | + |
| 302 | + @Test |
| 303 | + public void delayedTransportExitsIdleAfterEnter() throws Exception { |
| 304 | + // Start a new call that will go to the delayed transport |
| 305 | + ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT); |
| 306 | + call.start(mockCallListener, new Metadata()); |
| 307 | + deliverResolutionResult(); |
| 308 | + |
| 309 | + channel.enterIdle(); |
| 310 | + |
| 311 | + // Since we have a call in delayed transport, the call to enterIdle() should have resulted in |
| 312 | + // the channel going to idle mode and then immediately exiting. We confirm this by verifying |
| 313 | + // that the name resolver was started up twice - once when the call was first created and a |
| 314 | + // second time after exiting idle mode. |
| 315 | + verify(mockNameResolver, times(2)).start(any(NameResolver.Listener2.class)); |
| 316 | + } |
| 317 | + |
287 | 318 | @Test |
288 | 319 | public void realTransportsHoldsOffIdleness() throws Exception { |
289 | 320 | final EquivalentAddressGroup addressGroup = servers.get(1); |
@@ -332,6 +363,50 @@ public void realTransportsHoldsOffIdleness() throws Exception { |
332 | 363 | verify(mockLoadBalancer).shutdown(); |
333 | 364 | } |
334 | 365 |
|
| 366 | + @Test |
| 367 | + public void enterIdleWhileRealTransportInProgress() { |
| 368 | + final EquivalentAddressGroup addressGroup = servers.get(1); |
| 369 | + |
| 370 | + // Start a call, which goes to delayed transport |
| 371 | + ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT); |
| 372 | + call.start(mockCallListener, new Metadata()); |
| 373 | + |
| 374 | + // Verify that we have exited the idle mode |
| 375 | + ArgumentCaptor<Helper> helperCaptor = ArgumentCaptor.forClass(null); |
| 376 | + verify(mockLoadBalancerProvider).newLoadBalancer(helperCaptor.capture()); |
| 377 | + deliverResolutionResult(); |
| 378 | + Helper helper = helperCaptor.getValue(); |
| 379 | + |
| 380 | + // Create a subchannel for the real transport to happen on. |
| 381 | + Subchannel subchannel = createSubchannelSafely(helper, addressGroup, Attributes.EMPTY); |
| 382 | + requestConnectionSafely(helper, subchannel); |
| 383 | + MockClientTransportInfo t0 = newTransports.poll(); |
| 384 | + t0.listener.transportReady(); |
| 385 | + |
| 386 | + SubchannelPicker mockPicker = mock(SubchannelPicker.class); |
| 387 | + when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class))) |
| 388 | + .thenReturn(PickResult.withSubchannel(subchannel)); |
| 389 | + updateBalancingStateSafely(helper, READY, mockPicker); |
| 390 | + |
| 391 | + // Delayed transport creates real streams in the app executor |
| 392 | + executor.runDueTasks(); |
| 393 | + |
| 394 | + // Move transport to the in-use state |
| 395 | + t0.listener.transportInUse(true); |
| 396 | + |
| 397 | + // Now we enter Idle mode while real transport is happening |
| 398 | + channel.enterIdle(); |
| 399 | + |
| 400 | + // Verify that the name resolver and the load balance were shut down. |
| 401 | + verify(mockNameResolver).shutdown(); |
| 402 | + verify(mockLoadBalancer).shutdown(); |
| 403 | + |
| 404 | + // When there are no pending streams, the call to enterIdle() should stick and |
| 405 | + // we remain in idle mode. We verify this by making sure that the name resolver |
| 406 | + // was not started up more than once (the initial startup). |
| 407 | + verify(mockNameResolver, atMostOnce()).start(isA(NameResolver.Listener2.class)); |
| 408 | + } |
| 409 | + |
335 | 410 | @Test |
336 | 411 | public void updateSubchannelAddresses_newAddressConnects() { |
337 | 412 | ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT); |
|
0 commit comments