diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/BrambleTestCase.java b/bramble-api/src/test/java/org/briarproject/bramble/test/BrambleTestCase.java index 9fae78043..9ac8a7a40 100644 --- a/bramble-api/src/test/java/org/briarproject/bramble/test/BrambleTestCase.java +++ b/bramble-api/src/test/java/org/briarproject/bramble/test/BrambleTestCase.java @@ -1,17 +1,42 @@ package org.briarproject.bramble.test; -import java.lang.Thread.UncaughtExceptionHandler; +import org.junit.After; +import org.junit.Before; -import static org.junit.Assert.fail; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +import static java.util.logging.Logger.getLogger; public abstract class BrambleTestCase { + private static final Logger LOG = + getLogger(BrambleTestCase.class.getName()); + + @Nullable + protected volatile Throwable exceptionInBackgroundThread = null; + public BrambleTestCase() { // Ensure exceptions thrown on worker threads cause tests to fail UncaughtExceptionHandler fail = (thread, throwable) -> { - throwable.printStackTrace(); - fail(); + LOG.log(Level.WARNING, "Caught unhandled exception", throwable); + exceptionInBackgroundThread = throwable; }; Thread.setDefaultUncaughtExceptionHandler(fail); } + + @Before + public void beforeBrambleTestCase() { + exceptionInBackgroundThread = null; + } + + @After + public void afterBrambleTestCase() { + Throwable thrown = exceptionInBackgroundThread; + LOG.warning("background thread has thrown an exception unexpectedly"); + if (thrown != null) throw new AssertionError(thrown); + } } diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/ThreadExceptionTest.java b/bramble-api/src/test/java/org/briarproject/bramble/test/ThreadExceptionTest.java new file mode 100644 index 000000000..14a5d21bb --- /dev/null +++ b/bramble-api/src/test/java/org/briarproject/bramble/test/ThreadExceptionTest.java @@ -0,0 +1,36 @@ +package org.briarproject.bramble.test; + +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +public class ThreadExceptionTest extends BrambleTestCase { + + @Test(expected = AssertionError.class) + public void testAssertionErrorMakesTestCaseFail() { + // This is what BrambleTestCase does, too: + fail(); + } + + @Test + public void testExceptionInThreadMakesTestCaseFail() { + Thread t = new Thread(() -> { + System.out.println("thread before exception"); + throw new RuntimeException("boom"); + }); + + t.start(); + try { + t.join(); + System.out.println("joined thread"); + } catch (InterruptedException e) { + System.out.println("interrupted while joining thread"); + fail(); + } + + assertNotNull(exceptionInBackgroundThread); + exceptionInBackgroundThread = null; + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java index 952576c98..29131fe8b 100644 --- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTest.java @@ -986,6 +986,10 @@ public class IntroductionIntegrationTest @Test public void testIntroduceesRemovedCleanup() throws Exception { addListeners(true, true); + // The second introducee shouldn't respond to the introduction + // otherwise there would be a race between the response to the REQUEST + // and the delivery of the ABORT + listener2.answerRequests = false; // make introduction introductionManager0