diff --git a/packages/react-native/android/src/main/java/backtrace/library/BacktraceAndroidBackgroundUnhandledExceptionHandler.java b/packages/react-native/android/src/main/java/backtrace/library/BacktraceAndroidBackgroundUnhandledExceptionHandler.java index 12e4f835..863842b2 100644 --- a/packages/react-native/android/src/main/java/backtrace/library/BacktraceAndroidBackgroundUnhandledExceptionHandler.java +++ b/packages/react-native/android/src/main/java/backtrace/library/BacktraceAndroidBackgroundUnhandledExceptionHandler.java @@ -66,10 +66,8 @@ public void start(Callback callback) { @Override public synchronized void uncaughtException(final Thread thread, final Throwable throwable) { try { - if (throwable instanceof Exception) { - report(throwable); - } - } catch (RuntimeException ex) { + report(throwable); + } catch (RuntimeException | Error ex) { Log.w(LOG_TAG, "Failed to report the unhandled exception.", ex); } finally { Thread.UncaughtExceptionHandler rootHandler = _rootHandler; diff --git a/packages/react-native/android/src/test/java/backtraceio/library/BacktraceAndroidBackgroundUnhandledExceptionHandlerTest.java b/packages/react-native/android/src/test/java/backtraceio/library/BacktraceAndroidBackgroundUnhandledExceptionHandlerTest.java index 1df6d012..46734d40 100644 --- a/packages/react-native/android/src/test/java/backtraceio/library/BacktraceAndroidBackgroundUnhandledExceptionHandlerTest.java +++ b/packages/react-native/android/src/test/java/backtraceio/library/BacktraceAndroidBackgroundUnhandledExceptionHandlerTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import com.facebook.react.bridge.Callback; import java.util.ArrayList; @@ -139,17 +140,48 @@ public void failingCallbackStillForwards() { } @Test - public void errorIsForwardedWithoutConsumingTheCallback() { + public void stackOverflowErrorIsReportedAndForwarded() { + assertErrorReportedAndForwarded(new StackOverflowError("stack size 8MB")); + } + + @Test + public void outOfMemoryErrorIsReportedAndForwarded() { + assertErrorReportedAndForwarded(new OutOfMemoryError("Failed to allocate a 24 byte allocation")); + } + + @Test + public void noClassDefFoundErrorIsReportedAndForwarded() { + assertErrorReportedAndForwarded(new NoClassDefFoundError("com/example/Missing")); + } + + @Test + public void secondCrashIsForwardedWithoutReporting() { RecordingCallback callback = new RecordingCallback(handler); handler.start(callback); - crash(new StackOverflowError()); - crash(new RuntimeException("after error")); + crash(new StackOverflowError("first")); + crash(new RuntimeException("second")); assertEquals(1, callback.calls.size()); assertEquals(2, rootHandler.received.size()); } + @Test + public void callbackThrowingErrorStillForwards() { + StackOverflowError error = new StackOverflowError("boom"); + List calls = new ArrayList<>(); + handler.start(args -> { + calls.add(args); + throw new OutOfMemoryError("callback failed"); + }); + + crash(error); + + assertEquals(1, calls.size()); + assertEquals(1, rootHandler.received.size()); + assertSame(error, rootHandler.received.get(0)); + } + @Test public void stopBeforeStartKeepsCurrentHandler() { handler.stop(); @@ -190,6 +222,20 @@ public void reloadedInstanceStartingFirstForwardsThroughInvalidatedInstance() { assertEquals(1, rootHandler.received.size()); } + private void assertErrorReportedAndForwarded(Throwable error) { + RecordingCallback callback = new RecordingCallback(handler); + handler.start(callback); + + crash(error); + + assertEquals(1, callback.calls.size()); + assertEquals(error.getClass().getName(), callback.calls.get(0)[0]); + assertEquals(error.getMessage(), callback.calls.get(0)[1]); + assertTrue(((String) callback.calls.get(0)[2]).contains(getClass().getName())); + assertEquals(1, rootHandler.received.size()); + assertSame(error, rootHandler.received.get(0)); + } + private void crash(Throwable throwable) { Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), throwable); }