Bug 1340662 - Implement utility to run Roboelectric test code on a background thread
MozReview-Commit-ID: JOWug3ug1BE
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/TestRunner.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/background/testhelpers/TestRunner.java
@@ -28,16 +28,18 @@ import org.junit.runners.model.Initializ
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.FileFsFile;
import org.robolectric.res.FsFile;
import org.robolectric.util.Logger;
import org.robolectric.util.ReflectionHelpers;
+import java.util.LinkedList;
+
/**
* Test runner customized for running unit tests either through the Gradle CLI or
* Android Studio. The runner uses the build type and build flavor to compute the
* resource, asset, and AndroidManifest paths.
*
* This test runner requires that you set the 'constants' field on the @Config
* annotation (or the org.robolectric.Config.properties file) for your tests.
*
@@ -117,9 +119,52 @@ public class TestRunner extends Robolect
private String getBuildDir(Config config) {
try {
return ReflectionHelpers.getStaticField(config.constants(), "BUILD_DIR");
} catch (Throwable e) {
return null;
}
}
+
+ /**
+ * Run test code off the main thread, return once that code has finished. The supplied runnable
+ * will be run on a newly created thread. Any exceptions or assertion failures will be passed
+ * back to the main thread and rethrown there.
+ *
+ * Roboelectric runs all code on the main thread - this results in any code protected by
+ * ThreadUtils.assertNotOnUiThread() failing. This method exists in order to allow simpler
+ * testing of such code. This lets us avoid having to disable thread assertions when testing,
+ * or performing other hackier tricks.
+ *
+ * (Note that Robolectric.flushBackgroundThreadScheduler() runs queued tasks on the main thread,
+ * hence that isn't a solution that we can use.)
+ */
+ public static void runOnBackgroundThread(final Runnable testCode) throws Throwable {
+ final Thread thread = new Thread() {
+ @Override
+ public void run() {
+ testCode.run();
+ }
+ };
+
+ // Any exceptions that are thrown on a Thread will be silently ignored by default. We need to store
+ // the exception and rethrow it ourselves later. The Thread will terminate once an exception
+ // is thrown, so we don't need a list here - however we can't set references to new objects
+ // on different threads (i.e. we need a final reference here), and Optional<> isn't available
+ // until API 24 - so we just use a list as the simplest equivalent.
+ final LinkedList<Throwable> throwables = new LinkedList<>();
+
+ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ throwables.add(ex);
+ }
+ });
+
+ thread.start();
+ thread.join();
+
+ if (!throwables.isEmpty()) {
+ throw throwables.get(0);
+ }
+ }
}