未验证 提交 15f5696c 编写于 作者: X xster 提交者: GitHub

Add a java injector for testing (#20789)

上级 cb9439af
......@@ -694,6 +694,7 @@ FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_poo
FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool_unittests.cc
FILE: ../../../flutter/shell/platform/android/flutter_main.cc
FILE: ../../../flutter/shell/platform/android/flutter_main.h
FILE: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java
FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
......
......@@ -117,6 +117,7 @@ embedding_sources_jar_filename = "$embedding_artifact_id-sources.jar"
embedding_source_jar_path = "$root_out_dir/$embedding_sources_jar_filename"
android_java_sources = [
"io/flutter/FlutterInjector.java",
"io/flutter/Log.java",
"io/flutter/app/FlutterActivity.java",
"io/flutter/app/FlutterActivityDelegate.java",
......@@ -415,6 +416,7 @@ action("robolectric_tests") {
jar_path = "$root_out_dir/robolectric_tests.jar"
sources = [
"test/io/flutter/FlutterInjectorTest.java",
"test/io/flutter/FlutterTestSuite.java",
"test/io/flutter/SmokeTest.java",
"test/io/flutter/embedding/android/AndroidKeyProcessorTest.java",
......@@ -434,6 +436,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java",
"test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java",
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
"test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java",
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import io.flutter.embedding.engine.loader.FlutterLoader;
/**
* This class is a simple dependency injector for the relatively thin Android part of the Flutter
* engine.
*
* <p>This simple solution is used facilitate testability without bringing in heavier
* app-development centric dependency injection frameworks such as Guice or Dagger2 or spreading
* construction injection everywhere.
*/
public final class FlutterInjector {
private static FlutterInjector instance;
private static boolean accessed;
/**
* Use {@link FlutterInjector.Builder} to specify members to be injected via the static {@code
* FlutterInjector}.
*
* <p>This can only be called at the beginning of the program before the {@link #instance()} is
* accessed.
*/
public static void setInstance(@NonNull FlutterInjector injector) {
if (accessed) {
throw new IllegalStateException(
"Cannot change the FlutterInjector instance once it's been "
+ "read. If you're trying to dependency inject, be sure to do so at the beginning of "
+ "the program");
}
instance = injector;
}
/**
* Retrieve the static instance of the {@code FlutterInjector} to use in your program.
*
* <p>Once you access it, you can no longer change the values injected.
*
* <p>If no override is provided for the injector, reasonable defaults are provided.
*/
public static FlutterInjector instance() {
accessed = true;
if (instance == null) {
instance = new Builder().build();
}
return instance;
}
// This whole class is here to enable testing so to test the thing that lets you test, some degree
// of hack is needed.
@VisibleForTesting
public static void reset() {
accessed = false;
instance = null;
}
private FlutterInjector(boolean shouldLoadNative, @NonNull FlutterLoader flutterLoader) {
this.shouldLoadNative = shouldLoadNative;
this.flutterLoader = flutterLoader;
}
private boolean shouldLoadNative;
private FlutterLoader flutterLoader;
/**
* Returns whether the Flutter Android engine embedding should load the native C++ engine.
*
* <p>Useful for testing since JVM tests via Robolectric can't load native libraries.
*/
public boolean shouldLoadNative() {
return shouldLoadNative;
}
/** Returns the {@link FlutterLoader} instance to use for the Flutter Android engine embedding. */
@NonNull
public FlutterLoader flutterLoader() {
return flutterLoader;
}
/**
* Builder used to supply a custom FlutterInjector instance to {@link
* FlutterInjector#setInstance(FlutterInjector)}.
*
* <p>Non-overriden values have reasonable defaults.
*/
public static final class Builder {
private boolean shouldLoadNative = true;
/**
* Sets whether the Flutter Android engine embedding should load the native C++ engine.
*
* <p>Useful for testing since JVM tests via Robolectric can't load native libraries.
*
* <p>Defaults to true.
*/
public Builder setShouldLoadNative(boolean shouldLoadNative) {
this.shouldLoadNative = shouldLoadNative;
return this;
}
private FlutterLoader flutterLoader;
/**
* Sets a {@link FlutterLoader} override.
*
* <p>A reasonable default will be used if unspecified.
*/
public Builder setFlutterLoader(@NonNull FlutterLoader flutterLoader) {
this.flutterLoader = flutterLoader;
return this;
}
private void fillDefaults() {
if (flutterLoader == null) {
flutterLoader = new FlutterLoader();
}
}
/**
* Builds a {@link FlutterInjector} from the builder. Unspecified properties will have
* reasonable defaults.
*/
public FlutterInjector build() {
fillDefaults();
System.out.println("should load native is " + shouldLoadNative);
return new FlutterInjector(shouldLoadNative, flutterLoader);
}
}
}
......@@ -35,7 +35,8 @@ import io.flutter.view.FlutterView;
import java.util.ArrayList;
/**
* Class that performs the actual work of tying Android {@link Activity} instances to Flutter.
* Deprecated class that performs the actual work of tying Android {@link Activity} instances to
* Flutter.
*
* <p>This exists as a dedicated class (as opposed to being integrated directly into {@link
* FlutterActivity}) to facilitate applications that don't wish to subclass {@code FlutterActivity}.
......@@ -48,6 +49,10 @@ import java.util.ArrayList;
* FlutterActivityEvents} from your activity to an instance of this class. Optionally, you can make
* your activity implement {@link PluginRegistry} and/or {@link
* io.flutter.view.FlutterView.Provider} and forward those methods to this class as well.
*
* <p>Deprecation: {@link io.flutter.embedding.android.FlutterActivity} is the new API that now
* replaces this class and {@link io.flutter.app.FlutterActivity}. See
* https://flutter.dev/go/android-project-migration for more migration details.
*/
public final class FlutterActivityDelegate
implements FlutterActivityEvents, FlutterView.Provider, PluginRegistry {
......
......@@ -7,7 +7,7 @@ package io.flutter.app;
import android.app.Activity;
import android.app.Application;
import androidx.annotation.CallSuper;
import io.flutter.view.FlutterMain;
import io.flutter.FlutterInjector;
/**
* Flutter implementation of {@link android.app.Application}, managing application-level global
......@@ -21,7 +21,7 @@ public class FlutterApplication extends Application {
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
FlutterInjector.instance().flutterLoader().startInitialization(this);
}
private Activity mCurrentActivity = null;
......
......@@ -7,6 +7,7 @@ package io.flutter.embedding.engine;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.loader.FlutterLoader;
......@@ -131,7 +132,8 @@ public class FlutterEngine {
* <p>In order to pass Dart VM initialization arguments (see {@link
* io.flutter.embedding.engine.FlutterShellArgs}) when creating the VM, manually set the
* initialization arguments by calling {@link FlutterLoader#startInitialization(Context)} and
* {@link FlutterLoader#ensureInitializationComplete(Context, String[])}.
* {@link FlutterLoader#ensureInitializationComplete(Context, String[])} before constructing the
* engine.
*/
public FlutterEngine(@NonNull Context context) {
this(context, null);
......@@ -143,7 +145,7 @@ public class FlutterEngine {
* <p>If the Dart VM has already started, the given arguments will have no effect.
*/
public FlutterEngine(@NonNull Context context, @Nullable String[] dartVmArgs) {
this(context, FlutterLoader.getInstance(), new FlutterJNI(), dartVmArgs, true);
this(context, /* flutterLoader */ null, new FlutterJNI(), dartVmArgs, true);
}
/**
......@@ -158,7 +160,7 @@ public class FlutterEngine {
boolean automaticallyRegisterPlugins) {
this(
context,
FlutterLoader.getInstance(),
/* flutterLoader */ null,
new FlutterJNI(),
dartVmArgs,
automaticallyRegisterPlugins);
......@@ -189,7 +191,7 @@ public class FlutterEngine {
boolean waitForRestorationData) {
this(
context,
FlutterLoader.getInstance(),
/* flutterLoader */ null,
new FlutterJNI(),
new PlatformViewsController(),
dartVmArgs,
......@@ -206,7 +208,7 @@ public class FlutterEngine {
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI) {
this(context, flutterLoader, flutterJNI, null, true);
}
......@@ -219,7 +221,7 @@ public class FlutterEngine {
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins) {
......@@ -238,7 +240,7 @@ public class FlutterEngine {
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@NonNull PlatformViewsController platformViewsController,
@Nullable String[] dartVmArgs,
......@@ -256,7 +258,7 @@ public class FlutterEngine {
/** Fully configurable {@code FlutterEngine} constructor. */
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@NonNull PlatformViewsController platformViewsController,
@Nullable String[] dartVmArgs,
......@@ -280,6 +282,9 @@ public class FlutterEngine {
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
this.flutterJNI = flutterJNI;
if (flutterLoader == null) {
flutterLoader = FlutterInjector.instance().flutterLoader();
}
flutterLoader.startInitialization(context.getApplicationContext());
flutterLoader.ensureInitializationComplete(context, dartVmArgs);
......
......@@ -15,8 +15,8 @@ import java.util.*;
* <p>The term "shell" refers to the native code that adapts Flutter to different platforms.
* Flutter's Android Java code initializes a native "shell" and passes these arguments to that
* native shell when it is initialized. See {@link
* io.flutter.view.FlutterMain#ensureInitializationComplete(Context, String[])} for more
* information.
* io.flutter.embedding.engine.loader.FlutterLoader#ensureInitializationComplete(Context, String[])}
* for more information.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class FlutterShellArgs {
......
......@@ -8,12 +8,13 @@ import android.content.res.AssetManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StringCodec;
import io.flutter.view.FlutterCallbackInformation;
import io.flutter.view.FlutterMain;
import java.nio.ByteBuffer;
/**
......@@ -250,9 +251,19 @@ public class DartExecutor implements BinaryMessenger {
* that entrypoint and other assets required for Dart execution.
*/
public static class DartEntrypoint {
/**
* Create a DartEntrypoint pointing to the default Flutter assets location with a default Dart
* entrypoint.
*/
@NonNull
public static DartEntrypoint createDefault() {
return new DartEntrypoint(FlutterMain.findAppBundlePath(), "main");
FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader();
if (!flutterLoader.initialized()) {
throw new AssertionError(
"DartEntrypoints can only be created once a FlutterEngine is created.");
}
return new DartEntrypoint(flutterLoader.findAppBundlePath(), "main");
}
/** The path within the AssetManager where the app will look for assets. */
......
......@@ -15,6 +15,7 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.BuildConfig;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.util.PathUtils;
import io.flutter.view.VsyncWaiter;
......@@ -47,7 +48,10 @@ public class FlutterLoader {
* <p>The returned instance loads Flutter native libraries in the standard way. A singleton object
* is used instead of static methods to facilitate testing without actually running native library
* linking.
*
* @deprecated Use the {@link io.flutter.FlutterInjector} instead.
*/
@Deprecated
@NonNull
public static FlutterLoader getInstance() {
if (instance == null) {
......@@ -56,13 +60,6 @@ public class FlutterLoader {
return instance;
}
@NonNull
public static FlutterLoader getInstanceForTest(FlutterApplicationInfo flutterApplicationInfo) {
FlutterLoader loader = new FlutterLoader();
loader.flutterApplicationInfo = flutterApplicationInfo;
return loader;
}
private boolean initialized = false;
@Nullable private Settings settings;
private long initStartTimestampMillis;
......@@ -128,7 +125,9 @@ public class FlutterLoader {
public InitResult call() {
ResourceExtractor resourceExtractor = initResources(appContext);
System.loadLibrary("flutter");
if (FlutterInjector.instance().shouldLoadNative()) {
System.loadLibrary("flutter");
}
// Prefetch the default font manager as soon as possible on a background thread.
// It helps to reduce time cost of engine setup that blocks the platform thread.
......@@ -231,13 +230,15 @@ public class FlutterLoader {
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
FlutterJNI.nativeInit(
applicationContext,
shellArgs.toArray(new String[0]),
kernelPath,
result.appStoragePath,
result.engineCachesPath,
initTimeMillis);
if (FlutterInjector.instance().shouldLoadNative()) {
FlutterJNI.nativeInit(
applicationContext,
shellArgs.toArray(new String[0]),
kernelPath,
result.appStoragePath,
result.engineCachesPath,
initTimeMillis);
}
initialized = true;
} catch (Exception e) {
......@@ -293,6 +294,11 @@ public class FlutterLoader {
});
}
/** Returns whether the FlutterLoader has finished loading the native library. */
public boolean initialized() {
return initialized;
}
/** Extract assets out of the APK that need to be cached as uncompressed files on disk. */
private ResourceExtractor initResources(@NonNull Context applicationContext) {
ResourceExtractor resourceExtractor = null;
......
......@@ -7,6 +7,7 @@ package io.flutter.embedding.engine.plugins.shim;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
......@@ -14,7 +15,6 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView;
import io.flutter.view.TextureRegistry;
import java.util.HashSet;
......@@ -85,12 +85,12 @@ class ShimRegistrar implements PluginRegistry.Registrar, FlutterPlugin, Activity
@Override
public String lookupKeyForAsset(String asset) {
return FlutterMain.getLookupKeyForAsset(asset);
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset);
}
@Override
public String lookupKeyForAsset(String asset, String packageName) {
return FlutterMain.getLookupKeyForAsset(asset, packageName);
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset, packageName);
}
@Override
......
......@@ -8,7 +8,7 @@ import android.content.Context;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.loader.FlutterLoader;
/**
......@@ -42,10 +42,7 @@ public class FlutterMain {
* @param applicationContext The Android application context.
*/
public static void startInitialization(@NonNull Context applicationContext) {
if (isRunningInRobolectricTest) {
return;
}
FlutterLoader.getInstance().startInitialization(applicationContext);
FlutterInjector.instance().flutterLoader().startInitialization(applicationContext);
}
/**
......@@ -61,12 +58,9 @@ public class FlutterMain {
*/
public static void startInitialization(
@NonNull Context applicationContext, @NonNull Settings settings) {
if (isRunningInRobolectricTest) {
return;
}
FlutterLoader.Settings newSettings = new FlutterLoader.Settings();
newSettings.setLogTag(settings.getLogTag());
FlutterLoader.getInstance().startInitialization(applicationContext, newSettings);
FlutterInjector.instance().flutterLoader().startInitialization(applicationContext, newSettings);
}
/**
......@@ -79,10 +73,9 @@ public class FlutterMain {
*/
public static void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
if (isRunningInRobolectricTest) {
return;
}
FlutterLoader.getInstance().ensureInitializationComplete(applicationContext, args);
FlutterInjector.instance()
.flutterLoader()
.ensureInitializationComplete(applicationContext, args);
}
/**
......@@ -94,22 +87,20 @@ public class FlutterMain {
@Nullable String[] args,
@NonNull Handler callbackHandler,
@NonNull Runnable callback) {
if (isRunningInRobolectricTest) {
return;
}
FlutterLoader.getInstance()
FlutterInjector.instance()
.flutterLoader()
.ensureInitializationCompleteAsync(applicationContext, args, callbackHandler, callback);
}
@NonNull
public static String findAppBundlePath() {
return FlutterLoader.getInstance().findAppBundlePath();
return FlutterInjector.instance().flutterLoader().findAppBundlePath();
}
@Deprecated
@Nullable
public static String findAppBundlePath(@NonNull Context applicationContext) {
return FlutterLoader.getInstance().findAppBundlePath();
return FlutterInjector.instance().flutterLoader().findAppBundlePath();
}
/**
......@@ -121,7 +112,7 @@ public class FlutterMain {
*/
@NonNull
public static String getLookupKeyForAsset(@NonNull String asset) {
return FlutterLoader.getInstance().getLookupKeyForAsset(asset);
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset);
}
/**
......@@ -135,23 +126,6 @@ public class FlutterMain {
*/
@NonNull
public static String getLookupKeyForAsset(@NonNull String asset, @NonNull String packageName) {
return FlutterLoader.getInstance().getLookupKeyForAsset(asset, packageName);
}
private static boolean isRunningInRobolectricTest = false;
/*
* Indicates whether we are currently running in a Robolectric Test.
*
* <p> Flutter cannot be initialized inside a Robolectric environment since it cannot load
* native libraries.
*
* @deprecated Use the new embedding (io.flutter.embedding) instead which provides better
* modularity for testing.
*/
@Deprecated
@VisibleForTesting
public static void setIsRunningInRobolectricTest(boolean isRunningInRobolectricTest) {
FlutterMain.isRunningInRobolectricTest = isRunningInRobolectricTest;
return FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(asset, packageName);
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import io.flutter.embedding.engine.loader.FlutterLoader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class FlutterInjectorTest {
@Mock FlutterLoader mockFlutterLoader;
@Before
public void setUp() {
// Since the intent is to have a convenient static class to use for production.
FlutterInjector.reset();
MockitoAnnotations.initMocks(this);
}
@Test
public void itHasSomeReasonableDefaults() {
// Implicitly builds when first accessed.
FlutterInjector injector = FlutterInjector.instance();
assertNotNull(injector.flutterLoader());
assertTrue(injector.shouldLoadNative());
}
@Test
public void canPartiallyOverride() {
FlutterInjector.setInstance(
new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build());
FlutterInjector injector = FlutterInjector.instance();
assertEquals(injector.flutterLoader(), mockFlutterLoader);
assertTrue(injector.shouldLoadNative());
}
@Test()
public void cannotBeChangedOnceRead() {
FlutterInjector.instance();
assertThrows(
IllegalStateException.class,
() -> {
FlutterInjector.setInstance(
new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build());
});
}
}
......@@ -16,6 +16,8 @@ import io.flutter.embedding.engine.FlutterEnginePluginRegistryTest;
import io.flutter.embedding.engine.FlutterJNITest;
import io.flutter.embedding.engine.LocalizationPluginTest;
import io.flutter.embedding.engine.RenderingComponentTest;
import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest;
import io.flutter.embedding.engine.loader.FlutterLoaderTest;
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
import io.flutter.embedding.engine.systemchannels.KeyEventChannelTest;
......@@ -44,6 +46,7 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest;
@SuiteClasses({
AccessibilityBridgeTest.class,
AndroidKeyProcessorTest.class,
ApplicationInfoLoaderTest.class,
DartExecutorTest.class,
FlutterActivityAndFragmentDelegateTest.class,
FlutterActivityTest.class,
......@@ -53,8 +56,11 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest;
FlutterEngineTest.class,
FlutterFragmentActivityTest.class,
FlutterFragmentTest.class,
FlutterInjectorTest.class,
FlutterJNITest.class,
FlutterLaunchTests.class,
FlutterLoaderTest.class,
FlutterShellArgsTest.class,
FlutterRendererTest.class,
FlutterShellArgsTest.class,
FlutterViewTest.class,
......
......@@ -3,6 +3,7 @@ package test.io.flutter.embedding.engine;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
......@@ -10,6 +11,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
......@@ -137,4 +139,18 @@ public class FlutterEngineTest {
verify(context, atLeast(1)).getApplicationContext();
}
@Test
public void itCanUseFlutterLoaderInjectionViaFlutterInjector() {
FlutterInjector.reset();
FlutterLoader mockFlutterLoader = mock(FlutterLoader.class);
FlutterInjector.setInstance(
new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build());
Context mockContext = mock(Context.class);
new FlutterEngine(mockContext, null, flutterJNI);
verify(mockFlutterLoader, times(1)).startInitialization(any());
verify(mockFlutterLoader, times(1)).ensureInitializationComplete(any(), any());
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package test.io.flutter.embedding.engine;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import androidx.annotation.NonNull;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterApplicationInfo;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
......@@ -22,47 +24,19 @@ import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class PluginComponentTest {
@Before
public void setUp() {
FlutterInjector.reset();
}
@Test
public void pluginsCanAccessFlutterAssetPaths() {
// Setup test.
FlutterInjector.setInstance(new FlutterInjector.Builder().setShouldLoadNative(false).build());
FlutterJNI flutterJNI = mock(FlutterJNI.class);
when(flutterJNI.isAttached()).thenReturn(true);
FlutterApplicationInfo emptyInfo =
new FlutterApplicationInfo(null, null, null, null, null, null, false, false);
// FlutterLoader is the object to which the PluginRegistry defers for obtaining
// the path to a Flutter asset. Ideally in this component test we would use a
// real FlutterLoader and directly verify the relationship between FlutterAssets
// and FlutterLoader. However, a real FlutterLoader cannot be used in a JVM test
// because it would attempt to load native libraries. Therefore, we create a fake
// FlutterLoader, but then we defer the corresponding asset lookup methods to the
// real FlutterLoader singleton. This test ends up verifying that when FlutterAssets
// is queried for an asset path, it returns the real expected path based on real
// FlutterLoader behavior.
FlutterLoader flutterLoader = mock(FlutterLoader.class);
when(flutterLoader.getLookupKeyForAsset(any(String.class)))
.thenAnswer(
new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// Defer to a real FlutterLoader to return the asset path.
String fileNameOrSubpath = (String) invocation.getArguments()[0];
return FlutterLoader.getInstanceForTest(emptyInfo)
.getLookupKeyForAsset(fileNameOrSubpath);
}
});
when(flutterLoader.getLookupKeyForAsset(any(String.class), any(String.class)))
.thenAnswer(
new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// Defer to a real FlutterLoader to return the asset path.
String fileNameOrSubpath = (String) invocation.getArguments()[0];
String packageName = (String) invocation.getArguments()[1];
return FlutterLoader.getInstanceForTest(emptyInfo)
.getLookupKeyForAsset(fileNameOrSubpath, packageName);
}
});
FlutterLoader flutterLoader = new FlutterLoader();
// Execute behavior under test.
FlutterEngine flutterEngine =
......@@ -82,6 +56,7 @@ public class PluginComponentTest {
assertEquals(
"flutter_assets/packages/fakepackage/some/path/fake_asset.jpg",
plugin.getAssetPathBasedOnSubpathAndPackage());
FlutterInjector.reset();
}
private static class PluginThatAccessesAssets implements FlutterPlugin {
......
package test.io.flutter.embedding.engine.dart;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
......@@ -9,17 +11,31 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.AssetManager;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint;
import io.flutter.embedding.engine.loader.FlutterLoader;
import java.nio.ByteBuffer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class DartExecutorTest {
@Mock FlutterLoader mockFlutterLoader;
@Before
public void setUp() {
FlutterInjector.reset();
MockitoAnnotations.initMocks(this);
}
@Test
public void itSendsBinaryMessages() {
// Setup test.
......@@ -49,4 +65,24 @@ public class DartExecutorTest {
dartExecutor.notifyLowMemoryWarning();
verify(mockFlutterJNI, times(1)).notifyLowMemoryWarning();
}
@Test
public void itThrowsWhenCreatingADefaultDartEntrypointWithAnUninitializedFlutterLoader() {
assertThrows(
AssertionError.class,
() -> {
DartEntrypoint.createDefault();
});
}
@Test
public void itHasReasonableDefaultsWhenFlutterLoaderIsInitialized() {
when(mockFlutterLoader.initialized()).thenReturn(true);
when(mockFlutterLoader.findAppBundlePath()).thenReturn("my/custom/path");
FlutterInjector.setInstance(
new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build());
DartEntrypoint entrypoint = DartEntrypoint.createDefault();
assertEquals(entrypoint.pathToBundle, "my/custom/path");
assertEquals(entrypoint.dartEntrypointFunctionName, "main");
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.embedding.engine.loader;
import static org.junit.Assert.assertEquals;
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.embedding.engine.loader;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import io.flutter.FlutterInjector;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class FlutterLoaderTest {
@Before
public void setUp() {
FlutterInjector.reset();
}
@Test
public void itReportsUninitializedAfterCreating() {
FlutterLoader flutterLoader = new FlutterLoader();
assertFalse(flutterLoader.initialized());
}
@Test
public void itReportsInitializedAfterInitializing() {
FlutterInjector.setInstance(new FlutterInjector.Builder().setShouldLoadNative(false).build());
FlutterLoader flutterLoader = new FlutterLoader();
assertFalse(flutterLoader.initialized());
flutterLoader.startInitialization(RuntimeEnvironment.application);
flutterLoader.ensureInitializationComplete(RuntimeEnvironment.application, null);
assertTrue(flutterLoader.initialized());
FlutterInjector.reset();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册