Check/reset Thread.interrupted() after every test.
If a test is interrupted, subsequent tests using subclasses of
AbstractInterruptibleChannel will fail with
java.nio.channels.ClosedByInterruptException, e.g. often when opening
jar files.
Check at the end of every test.
diff --git a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
index 8de22df..b88c16c 100644
--- a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
+++ b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
@@ -405,6 +405,12 @@
@Override
protected void finallyAfterTest(FrameworkMethod method) {
+ // If the test was interrupted, it will interfere with new AbstractInterruptibleChannels in
+ // subsequent tests, e.g. created by Files.newInputStream(), so clear it and warn.
+ if (Thread.interrupted()) {
+ System.out.println("WARNING: Test thread was interrupted! " + method.toString());
+ }
+
try {
// reset static state afterward too, so statics don't defeat GC?
PerfStatsCollector.getInstance()
diff --git a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java
index 6783f20..8ab4201 100644
--- a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java
+++ b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java
@@ -11,7 +11,11 @@
import android.app.Application;
import android.os.Build;
import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
@@ -184,6 +188,13 @@
assertThat(metricNames).contains("initialization");
}
+ @Test
+ public void shouldResetThreadInterrupted() throws Exception {
+ RobolectricTestRunner runner = new MyRobolectricTestRunner(TestWithInterrupt.class);
+ runner.run(notifier);
+ assertThat(events).containsExactly("failure: failed for the right reason");
+ }
+
/////////////////////////////
public static class MyParallelUniverseWithFailingSetUp extends ParallelUniverse {
@@ -259,6 +270,27 @@
}
}
+ @Ignore
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public static class TestWithInterrupt {
+ @Test
+ public void first() throws Exception {
+ Thread.currentThread().interrupt();
+ }
+
+ @Test
+ public void second() throws Exception {
+ FileSystemProvider jarFSP = FileSystemProvider.installedProviders().stream()
+ .filter(p -> p.getScheme().equals("jar")).findFirst().get();
+ Path fakeJarFile = Paths.get(getClass().getResource("/resources.ap_").toURI());
+
+ // if Thread.interrupted() was true, this would fail in AbstractInterruptibleChannel:
+ jarFSP.newFileSystem(fakeJarFile, new HashMap<>());
+
+ fail("failed for the right reason");
+ }
+ }
+
private static class MyRobolectricTestRunner extends RobolectricTestRunner {
public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);