Skip to content

Commit 0f9d0f6

Browse files
committed
Add GrpcServerLifecycleTest
1 parent d39b809 commit 0f9d0f6

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright (c) 2016-2021 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.server.serverfactory;
19+
20+
import static java.time.Duration.ZERO;
21+
import static java.time.Duration.ofMillis;
22+
import static java.time.Duration.ofSeconds;
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
import static org.junit.jupiter.api.Assertions.assertFalse;
25+
import static org.junit.jupiter.api.Assertions.assertTimeout;
26+
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
27+
import static org.junit.jupiter.api.Assertions.assertTrue;
28+
import static org.junit.jupiter.api.Assertions.fail;
29+
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.reset;
31+
import static org.mockito.Mockito.when;
32+
33+
import java.io.IOException;
34+
import java.util.concurrent.CountDownLatch;
35+
import java.util.concurrent.TimeUnit;
36+
37+
import org.junit.jupiter.api.BeforeEach;
38+
import org.junit.jupiter.api.Test;
39+
import org.junit.jupiter.api.function.Executable;
40+
import org.opentest4j.AssertionFailedError;
41+
42+
import io.grpc.Server;
43+
44+
/**
45+
* Tests for {@link GrpcServerLifecycle}.
46+
*/
47+
class GrpcServerLifecycleTest {
48+
49+
private final GrpcServerFactory factory = mock(GrpcServerFactory.class);
50+
51+
@BeforeEach
52+
void beforeEach() {
53+
reset(this.factory);
54+
when(this.factory.getAddress()).thenReturn("test");
55+
when(this.factory.getPort()).thenReturn(-1);
56+
57+
}
58+
59+
@Test
60+
void testNoGraceShutdown() {
61+
// The server takes 5s seconds to shutdown
62+
final TestServer server = new TestServer(5000);
63+
when(this.factory.createServer()).thenReturn(server);
64+
65+
// But we won't wait
66+
final GrpcServerLifecycle lifecycle = new GrpcServerLifecycle(this.factory, ZERO);
67+
68+
lifecycle.start();
69+
70+
assertFalse(server.isShutdown());
71+
assertFalse(server.isTerminated());
72+
73+
// So the shutdown should complete near instantly
74+
assertTimeoutPreemptively(ofMillis(100), (Executable) lifecycle::stop);
75+
76+
assertTrue(server.isShutdown());
77+
assertTrue(server.isTerminated());
78+
}
79+
80+
@Test
81+
void testGracefulShutdown() {
82+
83+
// The server takes 2s seconds to shutdown
84+
final TestServer server = new TestServer(2000);
85+
when(this.factory.createServer()).thenReturn(server);
86+
87+
// And we give it 5s to shutdown
88+
final GrpcServerLifecycle lifecycle = new GrpcServerLifecycle(this.factory, ofMillis(5000));
89+
90+
lifecycle.start();
91+
92+
assertFalse(server.isShutdown());
93+
assertFalse(server.isTerminated());
94+
95+
// So it should finish within 5.1 seconds
96+
assertTimeout(ofMillis(5100), (Executable) lifecycle::stop);
97+
98+
assertTrue(server.isShutdown());
99+
assertTrue(server.isTerminated());
100+
101+
}
102+
103+
@Test
104+
void testAwaitShutdown() {
105+
106+
// The server takes 2s seconds to shutdown
107+
final TestServer server = new TestServer(5000);
108+
when(this.factory.createServer()).thenReturn(server);
109+
110+
// And we give it infinite time to shutdown
111+
final GrpcServerLifecycle lifecycle = new GrpcServerLifecycle(this.factory, ofSeconds(-1));
112+
113+
lifecycle.start();
114+
115+
assertFalse(server.isShutdown());
116+
assertFalse(server.isTerminated());
117+
118+
final long start = System.currentTimeMillis();
119+
120+
lifecycle.stop();
121+
122+
final long duration = System.currentTimeMillis() - start;
123+
// We waited for the entire duration
124+
assertThat(duration).isBetween(5000L, 5100L);
125+
126+
assertTrue(server.isShutdown());
127+
assertTrue(server.isTerminated());
128+
129+
}
130+
131+
@Test
132+
void testInterruptShutdown() {
133+
134+
// The server takes 60s seconds to shutdown
135+
final TestServer server = new TestServer(60000);
136+
when(this.factory.createServer()).thenReturn(server);
137+
138+
// And we give it infinite time to shutdown
139+
final GrpcServerLifecycle lifecycle = new GrpcServerLifecycle(this.factory, ofSeconds(-1));
140+
141+
lifecycle.start();
142+
143+
assertFalse(server.isShutdown());
144+
assertFalse(server.isTerminated());
145+
146+
try {
147+
// But we are in a hurry, so we interrupt it after 2s
148+
assertTimeoutPreemptively(ofMillis(2000), (Executable) lifecycle::stop);
149+
fail("Did not wait for shutdown to complete");
150+
} catch (final AssertionFailedError e) {
151+
// We failed due to the timeout/interrupt
152+
assertThat(e).getCause().matches(t -> "ExecutionTimeoutException".equals(t.getClass().getSimpleName()));
153+
}
154+
155+
// But the server is still properly terminated
156+
assertTrue(server.isShutdown());
157+
assertTrue(server.isTerminated());
158+
159+
}
160+
161+
public class TestServer extends Server {
162+
163+
private final long shutdownDelayMillis;
164+
165+
private boolean isShutdown = true;
166+
private CountDownLatch countDown = null;
167+
168+
public TestServer(final long shutdownDelayMillis) {
169+
this.shutdownDelayMillis = shutdownDelayMillis;
170+
}
171+
172+
@Override
173+
public Server start() throws IOException {
174+
this.countDown = new CountDownLatch(1);
175+
this.isShutdown = false;
176+
return this;
177+
}
178+
179+
@Override
180+
public Server shutdown() {
181+
this.isShutdown = true;
182+
final Thread t = new Thread(() -> {
183+
try {
184+
Thread.sleep(this.shutdownDelayMillis);
185+
} catch (final InterruptedException e) {
186+
Thread.currentThread().interrupt();
187+
}
188+
shutdownNow();
189+
});
190+
t.setName("test-server-shutdown-delay");
191+
t.setDaemon(true);
192+
t.start();
193+
return this;
194+
}
195+
196+
@Override
197+
public Server shutdownNow() {
198+
this.isShutdown = true;
199+
final CountDownLatch localCountDown = this.countDown;
200+
this.countDown = null;
201+
if (localCountDown != null) {
202+
localCountDown.countDown();
203+
}
204+
return this;
205+
}
206+
207+
@Override
208+
public boolean isShutdown() {
209+
return this.isShutdown;
210+
}
211+
212+
@Override
213+
public boolean isTerminated() {
214+
return this.countDown == null;
215+
}
216+
217+
@Override
218+
public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
219+
return this.countDown.await(timeout, unit);
220+
}
221+
222+
@Override
223+
public void awaitTermination() throws InterruptedException {
224+
this.countDown.await();
225+
}
226+
227+
}
228+
229+
}

0 commit comments

Comments
 (0)