Simplified shutdown/wait methods for unit testing
This commit is contained in:
parent
f2adf6c8b8
commit
8c9140b3f6
|
@ -23,8 +23,13 @@ package dorkbox.network;
|
||||||
import static dorkbox.network.connection.EndPoint.THREADGROUP_NAME;
|
import static dorkbox.network.connection.EndPoint.THREADGROUP_NAME;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
import ch.qos.logback.classic.Level;
|
||||||
|
@ -47,6 +52,14 @@ class BaseTest {
|
||||||
public static final int tcpPort = 54558;
|
public static final int tcpPort = 54558;
|
||||||
public static final int udpPort = 54779;
|
public static final int udpPort = 54779;
|
||||||
|
|
||||||
|
// wait minimum of 2 minutes before we automatically fail the unit test.
|
||||||
|
public static final long AUTO_FAIL_TIMEOUT = 120;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
private CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
private volatile Thread autoFailThread = null;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// we want our entropy generation to be simple (ie, no user interaction to generate)
|
// we want our entropy generation to be simple (ie, no user interaction to generate)
|
||||||
try {
|
try {
|
||||||
|
@ -56,8 +69,8 @@ class BaseTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile boolean fail_check;
|
private final List<EndPoint> endPointConnections = new CopyOnWriteArrayList<EndPoint>();
|
||||||
private final ArrayList<EndPoint> endPointConnections = new ArrayList<EndPoint>();
|
private volatile boolean isStopping = false;
|
||||||
|
|
||||||
public
|
public
|
||||||
BaseTest() {
|
BaseTest() {
|
||||||
|
@ -110,6 +123,9 @@ class BaseTest {
|
||||||
public
|
public
|
||||||
void addEndPoint(final EndPoint endPointConnection) {
|
void addEndPoint(final EndPoint endPointConnection) {
|
||||||
this.endPointConnections.add(endPointConnection);
|
this.endPointConnections.add(endPointConnection);
|
||||||
|
synchronized (lock) {
|
||||||
|
latch = new CountDownLatch(endPointConnections.size() + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,50 +137,65 @@ class BaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
void stopEndPoints(final int stopAfterMillis) {
|
void stopEndPoints(final long stopAfterMillis) {
|
||||||
ThreadGroup threadGroup = Thread.currentThread()
|
ThreadGroup threadGroup = Thread.currentThread()
|
||||||
.getThreadGroup();
|
.getThreadGroup();
|
||||||
final String name = threadGroup.getName();
|
final String name = threadGroup.getName();
|
||||||
|
|
||||||
if (name.contains(THREADGROUP_NAME)) {
|
if (name.contains(THREADGROUP_NAME)) {
|
||||||
// We have to ALWAYS run this in a new thread, BECAUSE if stopEndPoints() is called from a client/server thread, it will
|
// We have to ALWAYS run this in a new thread, BECAUSE if stopEndPoints() is called from a client/server thread, it will DEADLOCK
|
||||||
// DEADLOCK
|
|
||||||
final Thread thread = new Thread(threadGroup.getParent(), new Runnable() {
|
final Thread thread = new Thread(threadGroup.getParent(), new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
try {
|
stopEndPoints_(stopAfterMillis);
|
||||||
// not the best, but this works for our purposes. This is a TAD hacky, because we ALSO have to make sure that we
|
|
||||||
// ARE NOT in the same thread group as netty!
|
|
||||||
Thread.sleep(stopAfterMillis);
|
|
||||||
|
|
||||||
stopEndPoints(stopAfterMillis);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, "UnitTest shutdown");
|
}, "UnitTest shutdown"); // a different name for the thread
|
||||||
|
|
||||||
thread.setDaemon(true);
|
thread.setDaemon(true);
|
||||||
thread.start();
|
thread.start();
|
||||||
} else {
|
} else {
|
||||||
synchronized (this.endPointConnections) {
|
stopEndPoints_(stopAfterMillis);
|
||||||
for (EndPoint endPointConnection : this.endPointConnections) {
|
|
||||||
endPointConnection.stop();
|
|
||||||
endPointConnection.waitForShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.endPointConnections.clear();
|
|
||||||
this.endPointConnections.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void stopEndPoints_(final long stopAfterMillis) {
|
||||||
|
if (isStopping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isStopping = true;
|
||||||
|
|
||||||
|
// not the best, but this works for our purposes. This is a TAD hacky, because we ALSO have to make sure that we
|
||||||
|
// ARE NOT in the same thread group as netty!
|
||||||
|
try {
|
||||||
|
Thread.sleep(stopAfterMillis);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EndPoint endPointConnection : this.endPointConnections) {
|
||||||
|
|
||||||
|
endPointConnection.stop();
|
||||||
|
endPointConnection.waitForShutdown();
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// we start with "1", so make sure to end it
|
||||||
|
latch.countDown();
|
||||||
|
this.endPointConnections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for network client/server threads to shutdown on their own, BUT WILL ERROR (+ shutdown) if they take longer than 2 minutes.
|
* Wait for network client/server threads to shutdown on their own, BUT WILL ERROR (+ shutdown) if they take longer than 2 minutes.
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
void waitForThreads() {
|
void waitForThreads() {
|
||||||
waitForThreads(0);
|
waitForThreads(AUTO_FAIL_TIMEOUT/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,63 +204,48 @@ class BaseTest {
|
||||||
* @param stopAfterSeconds how many seconds to wait
|
* @param stopAfterSeconds how many seconds to wait
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
void waitForThreads(int stopAfterSeconds) {
|
void waitForThreads(long stopAfterSeconds) {
|
||||||
final int stopAfterMillis = stopAfterSeconds * 1000; // this must be in milliseconds
|
synchronized (lock) {
|
||||||
|
|
||||||
this.fail_check = false;
|
|
||||||
|
|
||||||
Thread thread = null;
|
|
||||||
if (!this.endPointConnections.isEmpty()) {
|
|
||||||
// make sure to run this thread in the MAIN thread group..
|
|
||||||
ThreadGroup threadGroup = Thread.currentThread()
|
|
||||||
.getThreadGroup();
|
|
||||||
if (threadGroup.getName()
|
|
||||||
.contains(THREADGROUP_NAME)) {
|
|
||||||
threadGroup = threadGroup.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
thread = new Thread(threadGroup, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
// not the best, but this works for our purposes. This is a TAD hacky, because we ALSO have to make sure that we
|
|
||||||
// ARE NOT in the same thread group as netty!
|
|
||||||
try {
|
|
||||||
if (stopAfterMillis > 0L) {
|
|
||||||
// if we specify a time, then we stop, otherwise we wait the timeout.
|
|
||||||
Thread.sleep(stopAfterMillis);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Thread.sleep(120 * 1000L); // wait minimum of 2 minutes before we automatically fail the unit test.
|
|
||||||
}
|
|
||||||
|
|
||||||
System.err.println("Test did not complete in a timely manner...");
|
|
||||||
BaseTest.this.fail_check = true;
|
|
||||||
stopEndPoints();
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "UnitTest timeout fail condition");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!this.endPointConnections.isEmpty()) {
|
try {
|
||||||
synchronized (this.endPointConnections) {
|
latch.await(stopAfterSeconds, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public
|
||||||
|
void setupFailureCheck() {
|
||||||
|
autoFailThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
// not the best, but this works for our purposes. This is a TAD hacky, because we ALSO have to make sure that we
|
||||||
|
// ARE NOT in the same thread group as netty!
|
||||||
try {
|
try {
|
||||||
this.endPointConnections.wait(stopAfterMillis);
|
Thread.sleep(AUTO_FAIL_TIMEOUT * 1000L);
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
// if the thread is interrupted, then it means we finished the test.
|
||||||
|
System.err.println("Test did not complete in a timely manner...");
|
||||||
|
|
||||||
|
stopEndPoints(0L);
|
||||||
|
fail("Test did not complete in a timely manner.");
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, "UnitTest timeout fail condition");
|
||||||
|
autoFailThread.setDaemon(true);
|
||||||
|
// autoFailThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
if (thread != null) {
|
@After
|
||||||
thread.interrupt();
|
public
|
||||||
}
|
void cancelFailureCheck() {
|
||||||
|
if (autoFailThread != null) {
|
||||||
if (this.fail_check) {
|
autoFailThread.interrupt();
|
||||||
fail("Test did not complete in a timely manner.");
|
autoFailThread = null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give sockets a chance to close before starting the next test.
|
// Give sockets a chance to close before starting the next test.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user