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