Skip to content

Commit a9a92c3

Browse files
committed
Pump Robolectric resume on background thread
1 parent a982806 commit a9a92c3

File tree

1 file changed

+61
-8
lines changed

1 file changed

+61
-8
lines changed

scripts/templates/HelloCodenameOneUiTest.java.tmpl

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ import org.robolectric.annotation.Config;
2323
import org.robolectric.annotation.LooperMode;
2424
import org.robolectric.shadows.ShadowLooper;
2525

26+
import java.util.concurrent.Callable;
27+
import java.util.concurrent.ExecutionException;
28+
import java.util.concurrent.ExecutorService;
29+
import java.util.concurrent.Executors;
30+
import java.util.concurrent.Future;
31+
import java.util.concurrent.TimeUnit;
32+
import java.util.concurrent.TimeoutException;
33+
2634
@RunWith(RobolectricTestRunner.class)
2735
@Config(sdk = 30)
2836
@LooperMode(LooperMode.Mode.LEGACY)
@@ -37,8 +45,8 @@ public class @MAIN_NAME@UiTest {
3745
log("Activity created (Display.isInitialized=" + Display.isInitialized() + ")");
3846
controller.start();
3947
log("Activity started");
40-
controller.resume();
41-
log("Activity resumed");
48+
resumeWithPumpedMainLooper(controller);
49+
log("Display initialized after resume: " + Display.isInitialized());
4250
controller.visible();
4351
log("Activity made visible");
4452

@@ -70,12 +78,48 @@ public class @MAIN_NAME@UiTest {
7078
assertTrue("Screenshot file should exist", screenshotFile.isFile());
7179
assertTrue("Screenshot file should not be empty", screenshotFile.length() > 0L);
7280
} finally {
73-
controller.pause();
74-
log("Activity paused");
75-
controller.stop();
76-
log("Activity stopped");
77-
controller.destroy();
78-
log("Activity destroyed");
81+
safelyInvokeLifecycle(controller::pause, "pause");
82+
safelyInvokeLifecycle(controller::stop, "stop");
83+
safelyInvokeLifecycle(controller::destroy, "destroy");
84+
}
85+
}
86+
87+
private static void resumeWithPumpedMainLooper(ActivityController<@MAIN_NAME@Stub> controller)
88+
throws InterruptedException, ExecutionException, TimeoutException {
89+
log("Preparing to resume activity with pumped main looper");
90+
ExecutorService executor = Executors.newSingleThreadExecutor();
91+
Future<Void> future = executor.submit(new Callable<Void>() {
92+
@Override
93+
public Void call() {
94+
log("Invoking controller.resume()");
95+
controller.resume();
96+
log("controller.resume() returned");
97+
return null;
98+
}
99+
});
100+
101+
long deadlineNanos = System.nanoTime() + TimeUnit.SECONDS.toNanos(60);
102+
int iterations = 0;
103+
try {
104+
while (!future.isDone()) {
105+
if (System.nanoTime() >= deadlineNanos) {
106+
log("controller.resume() did not finish before deadline; cancelling");
107+
future.cancel(true);
108+
throw new TimeoutException("controller.resume() timed out");
109+
}
110+
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
111+
if (++iterations % 50 == 0) {
112+
log("Still waiting for controller.resume() after " + iterations + " pump iterations");
113+
}
114+
Thread.sleep(20L);
115+
}
116+
future.get(5L, TimeUnit.SECONDS);
117+
log("Activity resumed");
118+
} catch (TimeoutException | InterruptedException | ExecutionException ex) {
119+
log("Failed while resuming activity: " + ex);
120+
throw ex;
121+
} finally {
122+
executor.shutdownNow();
79123
}
80124
}
81125

@@ -170,6 +214,15 @@ public class @MAIN_NAME@UiTest {
170214
appendLogLine(formatted);
171215
}
172216

217+
private static void safelyInvokeLifecycle(Runnable invocation, String name) {
218+
try {
219+
invocation.run();
220+
log("Activity " + name + " invoked");
221+
} catch (RuntimeException ex) {
222+
log("Lifecycle invocation " + name + " failed: " + ex);
223+
}
224+
}
225+
173226
private static synchronized void appendLogLine(String message) {
174227
File directory = resolveArtifactDirectoryForLogging();
175228
if (directory == null) {

0 commit comments

Comments
 (0)