1 /* 2 * Copyright 2018 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.testing; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertSame; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.AdditionalAnswers.delegatesTo; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyLong; 25 import static org.mockito.Mockito.doReturn; 26 import static org.mockito.Mockito.doThrow; 27 import static org.mockito.Mockito.inOrder; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.verify; 31 import static org.mockito.Mockito.verifyNoMoreInteractions; 32 33 import io.grpc.ManagedChannel; 34 import io.grpc.Server; 35 import io.grpc.internal.FakeClock; 36 import io.grpc.testing.GrpcCleanupRule.Resource; 37 import java.util.concurrent.TimeUnit; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.junit.rules.ExpectedException; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 import org.junit.runners.model.MultipleFailureException; 44 import org.junit.runners.model.Statement; 45 import org.mockito.InOrder; 46 47 /** 48 * Unit tests for {@link GrpcCleanupRule}. 49 */ 50 @RunWith(JUnit4.class) 51 public class GrpcCleanupRuleTest { 52 public static final FakeClock fakeClock = new FakeClock(); 53 54 @SuppressWarnings("deprecation") // https://github.com/grpc/grpc-java/issues/7467 55 @Rule 56 public ExpectedException thrown = ExpectedException.none(); 57 58 @Test registerChannelReturnSameChannel()59 public void registerChannelReturnSameChannel() { 60 ManagedChannel channel = mock(ManagedChannel.class); 61 assertSame(channel, new GrpcCleanupRule().register(channel)); 62 } 63 64 @Test registerServerReturnSameServer()65 public void registerServerReturnSameServer() { 66 Server server = mock(Server.class); 67 assertSame(server, new GrpcCleanupRule().register(server)); 68 } 69 70 @Test registerNullChannelThrowsNpe()71 public void registerNullChannelThrowsNpe() { 72 ManagedChannel channel = null; 73 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 74 75 thrown.expect(NullPointerException.class); 76 thrown.expectMessage("channel"); 77 78 grpcCleanup.register(channel); 79 } 80 81 @Test registerNullServerThrowsNpe()82 public void registerNullServerThrowsNpe() { 83 Server server = null; 84 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 85 86 thrown.expect(NullPointerException.class); 87 thrown.expectMessage("server"); 88 89 grpcCleanup.register(server); 90 } 91 92 @Test singleChannelCleanup()93 public void singleChannelCleanup() throws Throwable { 94 // setup 95 ManagedChannel channel = mock(ManagedChannel.class); 96 Statement statement = mock(Statement.class); 97 InOrder inOrder = inOrder(statement, channel); 98 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 99 100 // run 101 grpcCleanup.register(channel); 102 103 boolean awaitTerminationFailed = false; 104 try { 105 // will throw because channel.awaitTermination(long, TimeUnit) will return false; 106 grpcCleanup.apply(statement, null /* description*/).evaluate(); 107 } catch (AssertionError e) { 108 awaitTerminationFailed = true; 109 } 110 111 // verify 112 assertTrue(awaitTerminationFailed); 113 inOrder.verify(statement).evaluate(); 114 inOrder.verify(channel).shutdown(); 115 inOrder.verify(channel).awaitTermination(anyLong(), any(TimeUnit.class)); 116 inOrder.verify(channel).shutdownNow(); 117 } 118 119 @Test singleServerCleanup()120 public void singleServerCleanup() throws Throwable { 121 // setup 122 Server server = mock(Server.class); 123 Statement statement = mock(Statement.class); 124 InOrder inOrder = inOrder(statement, server); 125 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 126 127 // run 128 grpcCleanup.register(server); 129 130 boolean awaitTerminationFailed = false; 131 try { 132 // will throw because channel.awaitTermination(long, TimeUnit) will return false; 133 grpcCleanup.apply(statement, null /* description*/).evaluate(); 134 } catch (AssertionError e) { 135 awaitTerminationFailed = true; 136 } 137 138 // verify 139 assertTrue(awaitTerminationFailed); 140 inOrder.verify(statement).evaluate(); 141 inOrder.verify(server).shutdown(); 142 inOrder.verify(server).awaitTermination(anyLong(), any(TimeUnit.class)); 143 inOrder.verify(server).shutdownNow(); 144 } 145 146 @Test multiResource_cleanupGracefully()147 public void multiResource_cleanupGracefully() throws Throwable { 148 // setup 149 Resource resource1 = mock(Resource.class); 150 Resource resource2 = mock(Resource.class); 151 Resource resource3 = mock(Resource.class); 152 doReturn(true).when(resource1).awaitReleased(anyLong(), any(TimeUnit.class)); 153 doReturn(true).when(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 154 doReturn(true).when(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 155 156 Statement statement = mock(Statement.class); 157 InOrder inOrder = inOrder(statement, resource1, resource2, resource3); 158 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 159 160 // run 161 grpcCleanup.register(resource1); 162 grpcCleanup.register(resource2); 163 grpcCleanup.register(resource3); 164 grpcCleanup.apply(statement, null /* description*/).evaluate(); 165 166 // Verify. 167 inOrder.verify(statement).evaluate(); 168 169 inOrder.verify(resource3).cleanUp(); 170 inOrder.verify(resource2).cleanUp(); 171 inOrder.verify(resource1).cleanUp(); 172 173 inOrder.verify(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 174 inOrder.verify(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 175 inOrder.verify(resource1).awaitReleased(anyLong(), any(TimeUnit.class)); 176 177 inOrder.verifyNoMoreInteractions(); 178 179 verify(resource1, never()).forceCleanUp(); 180 verify(resource2, never()).forceCleanUp(); 181 verify(resource3, never()).forceCleanUp(); 182 } 183 184 @Test baseTestFails()185 public void baseTestFails() throws Throwable { 186 // setup 187 Resource resource = mock(Resource.class); 188 189 Statement statement = mock(Statement.class); 190 doThrow(new Exception()).when(statement).evaluate(); 191 192 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 193 194 // run 195 grpcCleanup.register(resource); 196 197 boolean baseTestFailed = false; 198 try { 199 grpcCleanup.apply(statement, null /* description*/).evaluate(); 200 } catch (Exception e) { 201 baseTestFailed = true; 202 } 203 204 // verify 205 assertTrue(baseTestFailed); 206 207 verify(resource).forceCleanUp(); 208 verifyNoMoreInteractions(resource); 209 210 verify(resource, never()).cleanUp(); 211 verify(resource, never()).awaitReleased(anyLong(), any(TimeUnit.class)); 212 } 213 214 @Test multiResource_awaitReleasedFails()215 public void multiResource_awaitReleasedFails() throws Throwable { 216 // setup 217 Resource resource1 = mock(Resource.class); 218 Resource resource2 = mock(Resource.class); 219 Resource resource3 = mock(Resource.class); 220 doReturn(true).when(resource1).awaitReleased(anyLong(), any(TimeUnit.class)); 221 doReturn(false).when(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 222 doReturn(true).when(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 223 224 Statement statement = mock(Statement.class); 225 InOrder inOrder = inOrder(statement, resource1, resource2, resource3); 226 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 227 228 // run 229 grpcCleanup.register(resource1); 230 grpcCleanup.register(resource2); 231 grpcCleanup.register(resource3); 232 233 boolean cleanupFailed = false; 234 try { 235 grpcCleanup.apply(statement, null /* description*/).evaluate(); 236 } catch (AssertionError e) { 237 cleanupFailed = true; 238 } 239 240 // verify 241 assertTrue(cleanupFailed); 242 243 inOrder.verify(statement).evaluate(); 244 245 inOrder.verify(resource3).cleanUp(); 246 inOrder.verify(resource2).cleanUp(); 247 inOrder.verify(resource1).cleanUp(); 248 249 inOrder.verify(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 250 inOrder.verify(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 251 inOrder.verify(resource1).awaitReleased(anyLong(), any(TimeUnit.class)); 252 inOrder.verify(resource2).forceCleanUp(); 253 254 inOrder.verifyNoMoreInteractions(); 255 256 verify(resource3, never()).forceCleanUp(); 257 verify(resource1, never()).forceCleanUp(); 258 } 259 260 @Test multiResource_awaitReleasedInterrupted()261 public void multiResource_awaitReleasedInterrupted() throws Throwable { 262 // setup 263 Resource resource1 = mock(Resource.class); 264 Resource resource2 = mock(Resource.class); 265 Resource resource3 = mock(Resource.class); 266 doReturn(true).when(resource1).awaitReleased(anyLong(), any(TimeUnit.class)); 267 doThrow(new InterruptedException()) 268 .when(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 269 doReturn(true).when(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 270 271 Statement statement = mock(Statement.class); 272 InOrder inOrder = inOrder(statement, resource1, resource2, resource3); 273 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 274 275 // run 276 grpcCleanup.register(resource1); 277 grpcCleanup.register(resource2); 278 grpcCleanup.register(resource3); 279 280 boolean cleanupFailed = false; 281 try { 282 grpcCleanup.apply(statement, null /* description*/).evaluate(); 283 } catch (Throwable e) { 284 cleanupFailed = true; 285 } 286 287 // verify 288 assertTrue(cleanupFailed); 289 assertTrue(Thread.interrupted()); 290 291 inOrder.verify(statement).evaluate(); 292 293 inOrder.verify(resource3).cleanUp(); 294 inOrder.verify(resource2).cleanUp(); 295 inOrder.verify(resource1).cleanUp(); 296 297 inOrder.verify(resource3).awaitReleased(anyLong(), any(TimeUnit.class)); 298 inOrder.verify(resource2).awaitReleased(anyLong(), any(TimeUnit.class)); 299 inOrder.verify(resource2).forceCleanUp(); 300 inOrder.verify(resource1).forceCleanUp(); 301 302 inOrder.verifyNoMoreInteractions(); 303 304 verify(resource3, never()).forceCleanUp(); 305 verify(resource1, never()).awaitReleased(anyLong(), any(TimeUnit.class)); 306 } 307 308 @Test multiResource_timeoutCalculation()309 public void multiResource_timeoutCalculation() throws Throwable { 310 // setup 311 312 Resource resource1 = mock(FakeResource.class, 313 delegatesTo(new FakeResource(1 /* cleanupNanos */, 10 /* awaitReleaseNanos */))); 314 315 Resource resource2 = mock(FakeResource.class, 316 delegatesTo(new FakeResource(100 /* cleanupNanos */, 1000 /* awaitReleaseNanos */))); 317 318 319 Statement statement = mock(Statement.class); 320 InOrder inOrder = inOrder(statement, resource1, resource2); 321 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule().setTicker(fakeClock.getTicker()); 322 323 // run 324 grpcCleanup.register(resource1); 325 grpcCleanup.register(resource2); 326 grpcCleanup.apply(statement, null /* description*/).evaluate(); 327 328 // verify 329 inOrder.verify(statement).evaluate(); 330 331 inOrder.verify(resource2).cleanUp(); 332 inOrder.verify(resource1).cleanUp(); 333 334 inOrder.verify(resource2).awaitReleased( 335 TimeUnit.SECONDS.toNanos(10) - 100 - 1, TimeUnit.NANOSECONDS); 336 inOrder.verify(resource1).awaitReleased( 337 TimeUnit.SECONDS.toNanos(10) - 100 - 1 - 1000, TimeUnit.NANOSECONDS); 338 339 inOrder.verifyNoMoreInteractions(); 340 341 verify(resource2, never()).forceCleanUp(); 342 verify(resource1, never()).forceCleanUp(); 343 } 344 345 @Test multiResource_timeoutCalculation_customTimeout()346 public void multiResource_timeoutCalculation_customTimeout() throws Throwable { 347 // setup 348 349 Resource resource1 = mock(FakeResource.class, 350 delegatesTo(new FakeResource(1 /* cleanupNanos */, 10 /* awaitReleaseNanos */))); 351 352 Resource resource2 = mock(FakeResource.class, 353 delegatesTo(new FakeResource(100 /* cleanupNanos */, 1000 /* awaitReleaseNanos */))); 354 355 356 Statement statement = mock(Statement.class); 357 InOrder inOrder = inOrder(statement, resource1, resource2); 358 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule() 359 .setTicker(fakeClock.getTicker()).setTimeout(3000, TimeUnit.NANOSECONDS); 360 361 // run 362 grpcCleanup.register(resource1); 363 grpcCleanup.register(resource2); 364 grpcCleanup.apply(statement, null /* description*/).evaluate(); 365 366 // verify 367 inOrder.verify(statement).evaluate(); 368 369 inOrder.verify(resource2).cleanUp(); 370 inOrder.verify(resource1).cleanUp(); 371 372 inOrder.verify(resource2).awaitReleased(3000 - 100 - 1, TimeUnit.NANOSECONDS); 373 inOrder.verify(resource1).awaitReleased(3000 - 100 - 1 - 1000, TimeUnit.NANOSECONDS); 374 375 inOrder.verifyNoMoreInteractions(); 376 377 verify(resource2, never()).forceCleanUp(); 378 verify(resource1, never()).forceCleanUp(); 379 } 380 381 @Test baseTestFailsThenCleanupFails()382 public void baseTestFailsThenCleanupFails() throws Throwable { 383 // setup 384 Exception baseTestFailure = new Exception("base test failure"); 385 Exception cleanupFailure = new RuntimeException("force cleanup failed"); 386 387 Statement statement = mock(Statement.class); 388 doThrow(baseTestFailure).when(statement).evaluate(); 389 390 Resource resource1 = mock(Resource.class); 391 Resource resource2 = mock(Resource.class); 392 Resource resource3 = mock(Resource.class); 393 doThrow(cleanupFailure).when(resource2).forceCleanUp(); 394 395 InOrder inOrder = inOrder(statement, resource1, resource2, resource3); 396 GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 397 398 // run 399 grpcCleanup.register(resource1); 400 grpcCleanup.register(resource2); 401 grpcCleanup.register(resource3); 402 403 Throwable failure = null; 404 try { 405 grpcCleanup.apply(statement, null /* description*/).evaluate(); 406 } catch (Throwable e) { 407 failure = e; 408 } 409 410 // verify 411 if (failure instanceof MultipleFailureException) { 412 // JUnit 4.13+ 413 assertThat(((MultipleFailureException) failure).getFailures()) 414 .containsExactly(baseTestFailure, cleanupFailure); 415 } else { 416 // JUnit 4.12. Suffers from https://github.com/junit-team/junit4/issues/1334 417 assertThat(failure).isSameInstanceAs(cleanupFailure); 418 } 419 420 inOrder.verify(statement).evaluate(); 421 inOrder.verify(resource3).forceCleanUp(); 422 inOrder.verify(resource2).forceCleanUp(); 423 inOrder.verifyNoMoreInteractions(); 424 425 verify(resource1, never()).cleanUp(); 426 verify(resource2, never()).cleanUp(); 427 verify(resource3, never()).cleanUp(); 428 verify(resource1, never()).forceCleanUp(); 429 } 430 431 public static class FakeResource implements Resource { 432 private final long cleanupNanos; 433 private final long awaitReleaseNanos; 434 FakeResource(long cleanupNanos, long awaitReleaseNanos)435 private FakeResource(long cleanupNanos, long awaitReleaseNanos) { 436 this.cleanupNanos = cleanupNanos; 437 this.awaitReleaseNanos = awaitReleaseNanos; 438 } 439 440 @Override cleanUp()441 public void cleanUp() { 442 fakeClock.forwardTime(cleanupNanos, TimeUnit.NANOSECONDS); 443 } 444 445 @Override forceCleanUp()446 public void forceCleanUp() { 447 } 448 449 @Override awaitReleased(long duration, TimeUnit timeUnit)450 public boolean awaitReleased(long duration, TimeUnit timeUnit) { 451 fakeClock.forwardTime(awaitReleaseNanos, TimeUnit.NANOSECONDS); 452 return true; 453 } 454 } 455 } 456