Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/PluginComponentTest.java",
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/dart/DartMessengerTest.java",
"test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java",
"test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java",
"test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public void handleMessageFromDart(
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message listener", ex);
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
} catch (Error err) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this catch all Throwables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, no. I wanted to be explicit about handling Errors which is a special kind of throwable that is not supposed to be caught. Java does not (currently) define any Throwable other than Error and Exception.

handleError(err);
}
} else {
Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
Expand All @@ -103,6 +105,8 @@ public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
} catch (Error err) {
handleError(err);
}
}
}
Expand All @@ -123,7 +127,19 @@ public int getPendingChannelResponseCount() {
return pendingReplies.size();
}

private static class Reply implements BinaryMessenger.BinaryReply {
// Handles `Error` objects which are not supposed to be caught.
//
// We forward them to the thread's uncaught exception handler if there is one. If not, they
// are rethrown.
private static void handleError(Error err) {
Thread currentThread = Thread.currentThread();
if (currentThread.getUncaughtExceptionHandler() == null) {
throw err;
}
currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, err);
}

static class Reply implements BinaryMessenger.BinaryReply {
@NonNull private final FlutterJNI flutterJNI;
private final int replyId;
private final AtomicBoolean done = new AtomicBoolean(false);
Expand Down
4 changes: 3 additions & 1 deletion shell/platform/android/test/io/flutter/FlutterTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import io.flutter.embedding.engine.FlutterJNITest;
import io.flutter.embedding.engine.LocalizationPluginTest;
import io.flutter.embedding.engine.RenderingComponentTest;
import io.flutter.embedding.engine.dart.DartExecutorTest;
import io.flutter.embedding.engine.dart.DartMessengerTest;
import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest;
import io.flutter.embedding.engine.loader.FlutterLoaderTest;
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest;
Expand All @@ -41,14 +43,14 @@
import test.io.flutter.embedding.engine.FlutterEngineTest;
import test.io.flutter.embedding.engine.FlutterShellArgsTest;
import test.io.flutter.embedding.engine.PluginComponentTest;
import test.io.flutter.embedding.engine.dart.DartExecutorTest;

@RunWith(Suite.class)
@SuiteClasses({
AccessibilityBridgeTest.class,
AndroidKeyProcessorTest.class,
ApplicationInfoLoaderTest.class,
DartExecutorTest.class,
DartMessengerTest.class,
FlutterActivityAndFragmentDelegateTest.class,
FlutterActivityTest.class,
FlutterAndroidComponentTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package test.io.flutter.embedding.engine.dart;
package io.flutter.embedding.engine.dart;

import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.flutter.embedding.engine.dart;

import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;

import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
import java.nio.ByteBuffer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class DartMessengerTest {
private static class ReportingUncaughtExceptionHandler
implements Thread.UncaughtExceptionHandler {
public Throwable latestException;

@Override
public void uncaughtException(Thread t, Throwable e) {
latestException = e;
}
}

@Test
public void itHandlesErrors() {
// Setup test.
final FlutterJNI fakeFlutterJni = mock(FlutterJNI.class);
final Thread currentThread = Thread.currentThread();
final Thread.UncaughtExceptionHandler savedHandler =
currentThread.getUncaughtExceptionHandler();
final ReportingUncaughtExceptionHandler reportingHandler =
new ReportingUncaughtExceptionHandler();
currentThread.setUncaughtExceptionHandler(reportingHandler);

// Create object under test.
final DartMessenger messenger = new DartMessenger(fakeFlutterJni);
final BinaryMessageHandler throwingHandler = mock(BinaryMessageHandler.class);
Mockito.doThrow(AssertionError.class)
.when(throwingHandler)
.onMessage(any(ByteBuffer.class), any(DartMessenger.Reply.class));

messenger.setMessageHandler("test", throwingHandler);
messenger.handleMessageFromDart("test", new byte[] {}, 0);
assertNotNull(reportingHandler.latestException);
assertTrue(reportingHandler.latestException instanceof AssertionError);
currentThread.setUncaughtExceptionHandler(savedHandler);
}
}