@@ -14,14 +14,17 @@ import java.io.FileOutputStream;
1414import java.io.FileWriter;
1515import java.io.IOException;
1616import java.io.PrintWriter;
17+ import java.io.StringWriter;
1718import java.util.concurrent.TimeUnit;
1819import org.junit.After;
1920import org.junit.Before;
2021import org.junit.Rule;
2122import org.junit.Test;
2223import org.junit.rules.TestRule;
2324import org.junit.rules.Timeout;
25+ import org.junit.rules.TestWatcher;
2426import org.junit.runner.RunWith;
27+ import org.junit.runner.Description;
2528import org.robolectric.Robolectric;
2629import org.robolectric.RobolectricTestRunner;
2730import org.robolectric.android.controller.ActivityController;
@@ -45,17 +48,58 @@ public class @MAIN_NAME@UiTest {
4548 .withLookingForStuckThread(true)
4649 .build();
4750
51+ @Rule
52+ public final TestWatcher loggingWatcher = new TestWatcher() {
53+ @Override
54+ protected void starting(Description description) {
55+ log("Starting test method: " + description.getDisplayName());
56+ }
57+
58+ @Override
59+ protected void succeeded(Description description) {
60+ log("Test method succeeded: " + description.getDisplayName());
61+ }
62+
63+ @Override
64+ protected void failed(Throwable e, Description description) {
65+ logError("Test method failed: " + description.getDisplayName(), e);
66+ dumpAllThreads("Failure diagnostics for " + description.getDisplayName());
67+ }
68+
69+ @Override
70+ protected void finished(Description description) {
71+ log("Finished test method: " + description.getDisplayName());
72+ }
73+ };
74+
4875 @Before
4976 public void setUp() {
5077 log("Starting Robolectric activity for screenshot test");
51- controller = Robolectric.buildActivity(@
[email protected] );
52- log("ActivityController created");
78+ try {
79+ controller = Robolectric.buildActivity(@
[email protected] );
80+ log("ActivityController created");
81+ } catch (RuntimeException ex) {
82+ logError("Failed to create ActivityController", ex);
83+ throw ex;
84+ }
5385 log("Invoking controller.setup() to create/start/resume activity");
54- activity = controller.setup().get();
55- log("controller.setup() completed (Display.isInitialized=" + Display.isInitialized() + ")");
86+ try {
87+ activity = controller.setup().get();
88+ log("controller.setup() completed (Display.isInitialized=" + Display.isInitialized()
89+ + ", activity=" + activity + ")");
90+ } catch (RuntimeException ex) {
91+ logError("controller.setup() threw an exception", ex);
92+ dumpAllThreads("controller.setup() failure diagnostics");
93+ throw ex;
94+ }
5695 log("Processing pending Codename One tasks after setup");
57- ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
58- log("Initial runUiThreadTasksIncludingDelayedTasks call finished");
96+ try {
97+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
98+ log("Initial runUiThreadTasksIncludingDelayedTasks call finished");
99+ } catch (RuntimeException ex) {
100+ logError("runUiThreadTasksIncludingDelayedTasks threw an exception", ex);
101+ throw ex;
102+ }
59103 }
60104
61105 @After
@@ -72,8 +116,13 @@ public class @MAIN_NAME@UiTest {
72116 public void mainFormScreenshotContainsRenderedContent() throws Exception {
73117 log("Running screenshot assertions with prepared activity");
74118 log("Idling main looper to process pending tasks");
75- ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
76- log("Completed runUiThreadTasksIncludingDelayedTasks");
119+ try {
120+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
121+ log("Completed runUiThreadTasksIncludingDelayedTasks");
122+ } catch (RuntimeException ex) {
123+ logError("runUiThreadTasksIncludingDelayedTasks during test threw an exception", ex);
124+ throw ex;
125+ }
77126
78127 if (Display.isInitialized()) {
79128 log("Codename One display reports current form="
@@ -189,6 +238,17 @@ public class @MAIN_NAME@UiTest {
189238 appendLogLine(formatted);
190239 }
191240
241+ private static void logError(String message, Throwable error) {
242+ log(message + ": " + error);
243+ StringWriter buffer = new StringWriter();
244+ error.printStackTrace(new PrintWriter(buffer));
245+ for (String line : buffer.toString().split("\r?\n")) {
246+ if (!line.isEmpty()) {
247+ log(line);
248+ }
249+ }
250+ }
251+
192252 private static void safelyInvokeLifecycle(Runnable invocation, String name) {
193253 try {
194254 invocation.run();
@@ -210,4 +270,19 @@ public class @MAIN_NAME@UiTest {
210270 System.out.println("[@MAIN_NAME@UiTest] Failed to persist log entry: " + ex.getMessage());
211271 }
212272 }
273+
274+ private static void dumpAllThreads(String reason) {
275+ log("Capturing thread dump: " + reason);
276+ Thread.getAllStackTraces().forEach((thread, stack) -> {
277+ log("Thread '" + thread.getName() + "' (id=" + thread.getId()
278+ + ", state=" + thread.getState() + ")");
279+ if (stack.length == 0) {
280+ log(" <no stack trace>");
281+ } else {
282+ for (StackTraceElement element : stack) {
283+ log(" at " + element);
284+ }
285+ }
286+ });
287+ }
213288}
0 commit comments