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