[go: nahoru, domu]

Relandx2 "[BrowserFragment] Add minimum setup to run instrumentation tests"

Reverted CL: crrev.com/c/3959680

Difference: PS1 -> PS2

Add a dependency would should hopefully resolve the r8 compilation errors on some builders.

> [BrowserFragment] Add minimum setup to run instrumentation tests
>
> The instrumentation tests run in the shell app and have access to the BF
> public API.
>
> This is based on the weblayer instrumentation tests.
>
> For now, the tests run with the support apk, and the BF shell in local
> mode.
>
> Also re-enable the unit tests.

Bug: 1375991

Change-Id: I6d99f3836a2343278a6bfc71f387f91dfb30e2b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3961474
Reviewed-by: Susanne Westphal <swestphal@chromium.org>
Reviewed-by: Erik Staab <estaab@chromium.org>
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1060469}
diff --git a/BUILD.gn b/BUILD.gn
index a6b18de..3f7b9ae 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -218,6 +218,13 @@
     ]
   }
 
+  if (is_android) {
+    deps += [
+      "//weblayer/browser/android/javatests:browserfragment_support_instrumentation_test_apk",
+      "//weblayer/browser/java:weblayer_junit_tests",
+    ]
+  }
+
   if (!is_ios && !is_android) {
     deps += [
       "//components/cronet:cronet_tests",
diff --git a/weblayer/browser/android/javatests/AndroidManifest.xml b/weblayer/browser/android/javatests/AndroidManifest.xml
index 58d3e4e3..09782a5 100644
--- a/weblayer/browser/android/javatests/AndroidManifest.xml
+++ b/weblayer/browser/android/javatests/AndroidManifest.xml
@@ -8,7 +8,7 @@
 
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.chromium.weblayer.tests">
+    package="org.chromium.browserfragment.tests">
   <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
   <!-- We add an application tag here just so that we can indicate that this
@@ -21,6 +21,6 @@
   </application>
 
   <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
-      android:targetPackage="org.chromium.weblayer.shell"
-      android:label="JUnit4-based tests for org.chromium.weblayer.shell" />
+      android:targetPackage="org.chromium.browserfragment.shell"
+      android:label="JUnit4-based tests for org.chromium.browserfragment.shell" />
 </manifest>
diff --git a/weblayer/browser/android/javatests/AndroidManifest_bundle.xml b/weblayer/browser/android/javatests/AndroidManifest_bundle.xml
deleted file mode 100644
index 90eba0d8..0000000
--- a/weblayer/browser/android/javatests/AndroidManifest_bundle.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright 2020 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.chromium.weblayer.tests">
-  <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
-  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
-  <!-- Starting from Android 11 not all packages are visible by default and this stops us
-       from starting an EmbeddedTestServer. Request visibility by using an intent
-       filter signature
-       (https://developer.android.com/training/package-visibility/declaring#intent-filter-signature)
-  -->
-  <queries>
-    <intent>
-      <action android:name="org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE" />
-    </intent>
-  </queries>
-
-  <!-- We add an application tag here just so that we can indicate that this
-       package needs to link against the android.test library, which is
-       needed when building test cases. -->
-  <application>
-      <activity android:name="org.chromium.test.broker.OnDeviceInstrumentationBroker"
-                android:exported="true"/>
-      <activity android:name="org.chromium.weblayer.shell.InstrumentationActivity"
-                android:theme="@style/Theme.AppCompat.DayNight" />
-  </application>
-
-  <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
-      android:targetPackage="org.chromium.weblayer.tests" />
-</manifest>
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 1043cf3..82fc909e0 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -6,55 +6,19 @@
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 
-android_library("weblayer_java_tests") {
+android_library("browserfragment_java_tests") {
   testonly = true
-  sources = [
-    "src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java",
-    "src/org/chromium/weblayer/test/BrowserTest.java",
-    "src/org/chromium/weblayer/test/CookieManagerTest.java",
-    "src/org/chromium/weblayer/test/CrashReporterTest.java",
-    "src/org/chromium/weblayer/test/DarkModeTest.java",
-    "src/org/chromium/weblayer/test/DataClearingTest.java",
-    "src/org/chromium/weblayer/test/DisplayCutoutTest.java",
-    "src/org/chromium/weblayer/test/DowngradeTest.java",
-    "src/org/chromium/weblayer/test/DownloadCallbackTest.java",
-    "src/org/chromium/weblayer/test/ErrorPageCallbackTest.java",
-    "src/org/chromium/weblayer/test/ExecuteScriptTest.java",
-    "src/org/chromium/weblayer/test/ExternalNavigationTest.java",
-    "src/org/chromium/weblayer/test/FaviconFetcherTest.java",
-    "src/org/chromium/weblayer/test/FindInPageTest.java",
-    "src/org/chromium/weblayer/test/FullscreenCallbackTest.java",
-    "src/org/chromium/weblayer/test/FullscreenSizeTest.java",
-    "src/org/chromium/weblayer/test/GoogleAccountsTest.java",
-    "src/org/chromium/weblayer/test/InputTypesTest.java",
-    "src/org/chromium/weblayer/test/MediaSessionTest.java",
-    "src/org/chromium/weblayer/test/NavigationTest.java",
-    "src/org/chromium/weblayer/test/NewTabCallbackTest.java",
-    "src/org/chromium/weblayer/test/OnTabRemovedTabListCallbackImpl.java",
-    "src/org/chromium/weblayer/test/PrerenderControllerTest.java",
-    "src/org/chromium/weblayer/test/ProfileTest.java",
-    "src/org/chromium/weblayer/test/RegisterExternalExperimentIdsTest.java",
-    "src/org/chromium/weblayer/test/ScrollOffsetCallbackTest.java",
-    "src/org/chromium/weblayer/test/SmokeTest.java",
-    "src/org/chromium/weblayer/test/TabCallbackTest.java",
-    "src/org/chromium/weblayer/test/TabListCallbackTest.java",
-    "src/org/chromium/weblayer/test/TabTest.java",
-    "src/org/chromium/weblayer/test/WebLayerLoadingTest.java",
-    "src/org/chromium/weblayer/test/WebLayerTest.java",
-    "src/org/chromium/weblayer/test/WebMessageTest.java",
-    "src/org/chromium/weblayer/test/WebViewCompatibilityTest.java",
-    "src/org/chromium/weblayer/test/XClientDataTest.java",
-  ]
+  sources = [ "src/org/chromium/browserfragment/test/BrowserFragmentTest.java" ]
   deps = [
-    ":weblayer_java_test_support",
+    ":browserfragment_java_test_support",
     "//base:base_java",
     "//base:base_java_test_support",
     "//components/browser_ui/share/android:java",
     "//components/safe_browsing/android:safe_browsing_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
-    "//net/android:net_java_test_support",
-    "//third_party/android_deps:espresso_java",
+    "//third_party/android_deps:com_google_guava_guava_android_java",
+    "//third_party/android_deps:com_google_guava_listenablefuture_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_activity_activity_java",
@@ -64,84 +28,19 @@
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/blink/public/common:common_java",
-    "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit:junit",
-    "//weblayer/browser/java:interfaces_java",
-    "//weblayer/public/java",
+    "//weblayer/public/java:browserfragment_java",
     "//weblayer/public/javatests:weblayer_public_javatests",
-    "//weblayer/shell/android:weblayer_shell_java",
+    "//weblayer/shell/android:browserfragment_shell_java",
   ]
 }
 
-android_library("weblayer_private_java_tests") {
+android_library("browserfragment_java_test_support") {
   testonly = true
   sources = [
-    "src/org/chromium/weblayer/test/AccessibilityTest.java",
-    "src/org/chromium/weblayer/test/AutofillTest.java",
-    "src/org/chromium/weblayer/test/BackgroundFetchTest.java",
-    "src/org/chromium/weblayer/test/BrowserControlsOffsetCallbackTest.java",
-    "src/org/chromium/weblayer/test/BrowserControlsTest.java",
-    "src/org/chromium/weblayer/test/ContentCaptureTest.java",
-    "src/org/chromium/weblayer/test/FullscreenCallbackPrivateTest.java",
-    "src/org/chromium/weblayer/test/GeolocationTest.java",
-    "src/org/chromium/weblayer/test/GoogleAccountAccessTokenFetcherTest.java",
-    "src/org/chromium/weblayer/test/InfoBarTest.java",
-    "src/org/chromium/weblayer/test/MediaCaptureTest.java",
-    "src/org/chromium/weblayer/test/MediaRouterTest.java",
-    "src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java",
-    "src/org/chromium/weblayer/test/PageInfoTest.java",
-    "src/org/chromium/weblayer/test/PopupTest.java",
-    "src/org/chromium/weblayer/test/ResourceLoadingTest.java",
-    "src/org/chromium/weblayer/test/SiteSettingsTest.java",
-    "src/org/chromium/weblayer/test/TabPrivateTest.java",
-    "src/org/chromium/weblayer/test/TranslateTest.java",
-    "src/org/chromium/weblayer/test/UrlBarControllerTest.java",
-    "src/org/chromium/weblayer/test/WebAuthnTest.java",
-  ]
-  deps = [
-    ":weblayer_java_private_test_support",
-    ":weblayer_java_test_support",
-    "//base:base_java",
-    "//base:base_java_test_support",
-    "//content/public/android:content_java",
-    "//content/public/test/android:content_java_test_support",
-    "//net/android:net_java_test_support",
-    "//third_party/android_deps:espresso_java",
-    "//third_party/android_sdk:android_test_base_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_core_core_java",
-    "//third_party/androidx:androidx_fragment_fragment_java",
-    "//third_party/androidx:androidx_test_runner_java",
-    "//third_party/google-truth:google_truth_java",
-    "//third_party/hamcrest:hamcrest_java",
-    "//third_party/junit:junit",
-    "//ui/android:ui_java_test_support",
-    "//weblayer/browser/java:test_interfaces_java",
-    "//weblayer/browser/java:upstream_java",
-    "//weblayer/public/java",
-    "//weblayer/public/javatestutil:test_java",
-    "//weblayer/shell/android:weblayer_shell_java",
-  ]
-
-  srcjar_deps = [ "//weblayer/browser/java:weblayer_product_config" ]
-}
-
-android_library("weblayer_java_test_support") {
-  testonly = true
-  sources = [
-    "src/org/chromium/weblayer/test/BoundedCountDownLatch.java",
-    "src/org/chromium/weblayer/test/EventUtils.java",
-    "src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java",
-    "src/org/chromium/weblayer/test/MinWebLayerVersion.java",
-    "src/org/chromium/weblayer/test/MinWebLayerVersionSkipCheck.java",
-    "src/org/chromium/weblayer/test/NavigationWaiter.java",
-    "src/org/chromium/weblayer/test/NewTabCallbackImpl.java",
-    "src/org/chromium/weblayer/test/ResourceUtil.java",
-    "src/org/chromium/weblayer/test/TestFullscreenCallback.java",
-    "src/org/chromium/weblayer/test/WebLayerActivityTestRule.java",
-    "src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java",
+    "src/org/chromium/browserfragment/test/BrowserFragmentActivityTestRule.java",
+    "src/org/chromium/browserfragment/test/BrowserFragmentJUnit4ClassRunner.java",
+    "src/org/chromium/browserfragment/test/InstrumentationActivityTestRule.java",
   ]
   deps = [
     "//base:base_java",
@@ -151,35 +50,14 @@
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
-    "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit:junit",
     "//ui/android:ui_java_test_support",
-    "//weblayer/public/java",
-    "//weblayer/shell/android:weblayer_shell_java",
+    "//weblayer/public/java:browserfragment_java",
+    "//weblayer/shell/android:browserfragment_shell_java",
   ]
 }
 
-android_library("weblayer_java_private_test_support") {
-  testonly = true
-  sources = [
-    "src/org/chromium/weblayer/test/BrowserControlsHelper.java",
-    "src/org/chromium/weblayer/test/SettingsActivityTestRule.java",
-  ]
-  deps = [
-    ":weblayer_java_test_support",
-    "//base:base_java",
-    "//base:base_java_test_support",
-    "//content/public/test/android:content_java_test_support",
-    "//third_party/android_support_test_runner:runner_java",
-    "//third_party/hamcrest:hamcrest_java",
-    "//third_party/junit:junit",
-    "//weblayer/public/java",
-    "//weblayer/public/javatestutil:test_java",
-    "//weblayer/shell/android:weblayer_shell_java",
-  ]
-}
-
-template("weblayer_instrumentation") {
+template("browserfragment_instrumentation") {
   instrumentation_test_apk(target_name) {
     forward_variables_from(invoker, "*")
 
@@ -198,162 +76,13 @@
   }
 }
 
-# Runs the instrumentation tests loading the implementation from the default
-# WebView provider. This is the loading path that is used in production.
-# TODO(crbug.com/1033070): Figure out why the test infrastructure fails on some
-# Android versions.
-weblayer_instrumentation("weblayer_instrumentation_test_apk") {
-  apk_name = "WebLayerInstrumentationTest"
-  apk_under_test = "//weblayer/shell/android:weblayer_shell_system_webview_apk"
-  use_webview_provider = system_webview_apk_target
-
-  deps = [ ":weblayer_java_tests" ]
-}
-
-# This target compiles Chrome as well as the WebLayer instrumentation test
-# apk. Use this target for builders that use devices which do not have
-# Chrome installed like Android Marshmallow x86 emulators. If your device
-# does not have Chrome installed, then you can use this target when running
-# tests locally also.
-weblayer_instrumentation("weblayer_instrumentation_test_with_chrome_apk") {
-  apk_name = "WebLayerInstrumentationTestWithChrome"
-  apk_under_test = "//weblayer/shell/android:weblayer_shell_system_webview_apk"
-  use_webview_provider = system_webview_apk_target
-
-  deps = [
-    ":weblayer_java_tests",
-    "//chrome/android/:chrome_public_apk",
-  ]
-
-  data_deps = [ "//chrome/android/:chrome_public_apk" ]
-}
-
 # Runs the instrumentation tests loading the implementation from the WebLayer
 # support library.
-weblayer_instrumentation("weblayer_support_instrumentation_test_apk") {
-  apk_name = "WebLayerSupportInstrumentationTest"
-  apk_under_test = "//weblayer/shell/android:weblayer_shell_apk"
+browserfragment_instrumentation(
+    "browserfragment_support_instrumentation_test_apk") {
+  apk_name = "BrowserFragmentSupportInstrumentationTest"
+  apk_under_test = "//weblayer/shell/android:browserfragment_shell_local_apk"
   additional_apks = [ "//weblayer/shell/android:weblayer_support_apk" ]
 
-  deps = [ ":weblayer_java_tests" ]
-}
-
-weblayer_instrumentation("weblayer_private_instrumentation_test_apk") {
-  apk_name = "WebLayerPrivateInstrumentationTest"
-  apk_under_test = "//weblayer/shell/android:weblayer_shell_apk"
-  additional_apks = [
-    "//weblayer/shell/android:weblayer_support_apk",
-    "//components/media_router/test/android/media_router_test_support:media_router_test_support_apk",
-  ]
-
-  deps = [ ":weblayer_private_java_tests" ]
-}
-
-# Runs WebLayer instrumentation tests against arbitrary versions of the
-# client and implementation libraries.
-#
-# Example usage, testing M80 tests and client against master implementation:
-#   autoninja -C out/Release weblayer_instrumentation_test_versions_apk
-#   cipd install --root /tmp/M80 chromium/testing/weblayer-x86 m80
-#   out/Release/bin/run_weblayer_instrumentation_tests_versions_apk \
-#       --test-runner-outdir out/Release
-#       --client-outdir /tmp/M80/out/Release
-#       --implementation-outdir out/Release
-copy("weblayer_skew_tests") {
-  testonly = true
-
-  sources = [ "weblayer_instrumentation_test_versions.py" ]
-
-  outputs = [ "$root_build_dir/bin/run_weblayer_skew_tests" ]
-
-  # Also explicitly list the output as data so it gets isolated on bots.
-  data = [
-    "$root_build_dir/bin/run_weblayer_skew_tests",
-    "//third_party/catapult/third_party/typ/",
-    "//weblayer/browser/android/javatests/skew/expectations.txt",
-  ]
-
-  deps = [ ":weblayer_instrumentation_test_apk" ]
-
-  data_deps = [ ":weblayer_instrumentation_test_apk" ]
-}
-
-# Runs WebLayer instrumentation tests against arbitrary versions of the
-# client and implementation libraries. This target also compiles
-# Chrome for devices that do not come with Chrome installed. For usage
-# please see the comment above.
-copy("weblayer_skew_tests_with_chrome") {
-  testonly = true
-
-  sources = [ "weblayer_instrumentation_test_versions.py" ]
-
-  outputs = [ "$root_build_dir/bin/run_weblayer_skew_tests_with_chrome" ]
-
-  # Also explicitly list the output as data so it gets isolated on bots.
-  data = [
-    "$root_build_dir/bin/run_weblayer_skew_tests_with_chrome",
-    "//third_party/catapult/third_party/typ/",
-    "//weblayer/browser/android/javatests/skew/expectations.txt",
-  ]
-
-  deps = [
-    ":weblayer_instrumentation_test_apk",
-    "//chrome/android/:chrome_public_apk",
-  ]
-
-  data_deps = [
-    ":weblayer_instrumentation_test_apk",
-    "//chrome/android/:chrome_public_apk",
-  ]
-}
-
-android_test_apk("weblayer_bundle_test_apk") {
-  apk_name = "WebLayerBundleTest"
-  android_manifest = "AndroidManifest_bundle.xml"
-  sources = [
-    "src/org/chromium/weblayer/test/BundleLanguageTest.java",
-
-    # This is run both in the bundle tests and the standard instrumentation
-    # tests to make sure bundles don't do anything weird with WebLayer/WebView
-    # compatibility.
-    "src/org/chromium/weblayer/test/WebViewCompatibilityTest.java",
-  ]
-  deps = [
-    ":weblayer_java_test_support",
-    "//base:base_java",
-    "//base:base_java_test_support",
-    "//content/public/test/android:content_java_test_support",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
-    "//third_party/androidx:androidx_test_runner_java",
-    "//third_party/junit:junit",
-    "//weblayer/public/java",
-    "//weblayer/public/javatestutil:test_java",
-    "//weblayer/shell/android:weblayer_shell_java",
-  ]
-  never_incremental = true
-}
-
-instrumentation_test_runner("weblayer_bundle_test") {
-  if (android_64bit_target_cpu && skip_secondary_abi_for_cq) {
-    apk_under_test = "//android_webview:system_webview_64_bundle_apks"
-  } else {
-    apk_under_test = "//android_webview:system_webview_bundle_apks"
-  }
-
-  # TODO(cduvall): use_webview_provider is needed to make sure a custom WebView
-  # is installable. Unfortunately, bundles are not currently supported for the
-  # use_webview_provider arg, so the APK is specified. It will end up being
-  # replaced by the bundle in apk_under_test.
-  use_webview_provider = system_webview_apk_target
-  data_deps = [ system_webview_apk_target ]
-
-  additional_locales = [
-    "es-ES",
-    "fr-CA",
-  ]
-  android_test_apk = ":weblayer_bundle_test_apk"
-  additional_apks = [ "//net/android:net_test_support_apk" ]
-  data = [ "//weblayer/test/data/" ]
-  never_incremental = true
+  deps = [ ":browserfragment_java_tests" ]
 }
diff --git a/weblayer/browser/android/javatests/DEPS b/weblayer/browser/android/javatests/DEPS
index aee953057..ce42e25 100644
--- a/weblayer/browser/android/javatests/DEPS
+++ b/weblayer/browser/android/javatests/DEPS
@@ -2,12 +2,12 @@
   "-content/public",
   "+content/public/test",
 
-  # WebLayerJUnit4ClassRunner should be used for all tests since it has logic to
+  # BrowserFragmentJUnit4ClassRunner should be used for all tests since it has logic to
   # make command line flags work in WebLayer.
   "-base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
 ]
 specific_include_rules = {
-  "WebLayerJUnit4ClassRunner.java": [
+  "BrowserFragmentJUnit4ClassRunner.java": [
     "+base/test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
   ]
 }
diff --git a/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentActivityTestRule.java
new file mode 100644
index 0000000..a60da4483
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentActivityTestRule.java
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.browserfragment.test;
+
+import android.app.Activity;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.test.BaseActivityTestRule;
+
+import java.io.File;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Base ActivityTestRule for BrowserFragment instrumentation tests.
+ *
+ * This rule contains some common setup needed to deal with BrowserFragment's multiple classloaders.
+ */
+abstract class BrowserFragmentActivityTestRule<T extends Activity> extends BaseActivityTestRule<T> {
+    private static final String COMMAND_LINE_FILE = "weblayer-command-line";
+
+    public BrowserFragmentActivityTestRule(Class<T> clazz) {
+        super(clazz);
+    }
+
+    /**
+     * Writes the command line file. This can be useful if a test needs to dynamically add command
+     * line arguments before WebLayer has been loaded.
+     */
+    public void writeCommandLineFile() throws Exception {
+        // The CommandLine instance we have here will not be picked up in the
+        // implementation since they use different class loaders, so we need to write
+        // all the switches to the WebLayer command line file.
+        try (Writer writer = new OutputStreamWriter(
+                     InstrumentationRegistry.getInstrumentation().getTargetContext().openFileOutput(
+                             COMMAND_LINE_FILE, Context.MODE_PRIVATE),
+                     "UTF-8")) {
+            writer.write(TextUtils.join(" ", CommandLine.getJavaSwitchesOrNull()));
+        }
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    writeCommandLineFile();
+                    base.evaluate();
+                } finally {
+                    new File(InstrumentationRegistry.getInstrumentation()
+                                     .getTargetContext()
+                                     .getFilesDir(),
+                            COMMAND_LINE_FILE)
+                            .delete();
+                }
+            }
+        };
+    }
+}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentJUnit4ClassRunner.java b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentJUnit4ClassRunner.java
new file mode 100644
index 0000000..ebdebd0
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentJUnit4ClassRunner.java
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.browserfragment.test;
+
+import org.junit.runners.model.InitializationError;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.SkipCheck;
+
+import java.util.List;
+
+/**
+ * A custom runner for //weblayer JUnit4 tests.
+ */
+public class BrowserFragmentJUnit4ClassRunner extends BaseJUnit4ClassRunner {
+    /**
+     * Create a BrowserFragmentJUnit4ClassRunner to run {@code klass} and initialize values.
+     *
+     * @throws InitializationError if the test class malformed
+     */
+    public BrowserFragmentJUnit4ClassRunner(final Class<?> klass) throws InitializationError {
+        super(klass);
+    }
+
+    @Override
+    protected List<SkipCheck> getSkipChecks() {
+        // TODO(rayankans): Add SkipChecks.
+        return super.getSkipChecks();
+    }
+}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentTest.java b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentTest.java
new file mode 100644
index 0000000..1e24169
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/BrowserFragmentTest.java
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.browserfragment.test;
+
+import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlockingNoException;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.Batch;
+import org.chromium.browserfragment.Browser;
+import org.chromium.browserfragment.shell.InstrumentationActivity;
+
+/**
+ * Tests that fragment lifecycle works as expected.
+ */
+@RunWith(BrowserFragmentJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
+public class BrowserFragmentTest {
+    @Rule
+    public InstrumentationActivityTestRule mActivityTestRule =
+            new InstrumentationActivityTestRule();
+
+    private Browser mBrowser;
+
+    @Before
+    public void setUp() throws Throwable {
+        InstrumentationActivity activity = mActivityTestRule.launchShell();
+        mBrowser = activity.getBrowserFuture().get();
+    }
+
+    @Test
+    @SmallTest
+    public void successfullyCreateFragment() {
+        Assert.assertNotNull(mBrowser);
+        Assert.assertNotNull(runOnUiThreadBlockingNoException(() -> mBrowser.createFragment()));
+    }
+}
diff --git a/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/InstrumentationActivityTestRule.java
new file mode 100644
index 0000000..84cebd7
--- /dev/null
+++ b/weblayer/browser/android/javatests/src/org/chromium/browserfragment/test/InstrumentationActivityTestRule.java
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.browserfragment.test;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Rule;
+
+import org.chromium.browserfragment.shell.InstrumentationActivity;
+import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.net.test.EmbeddedTestServerRule;
+
+/**
+ * ActivityTestRule for InstrumentationActivity.
+ *
+ * Test can use this ActivityTestRule to launch or get InstrumentationActivity.
+ */
+public class InstrumentationActivityTestRule
+        extends BrowserFragmentActivityTestRule<InstrumentationActivity> {
+    @Rule
+    private EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
+
+    public InstrumentationActivityTestRule() {
+        super(InstrumentationActivity.class);
+    }
+
+    /**
+     * Starts the BrowserFragment Instrumentation activity.
+     */
+    public InstrumentationActivity launchShell() {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setComponent(
+                new ComponentName(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                        InstrumentationActivity.class));
+        launchActivity(intent);
+        return getActivity();
+    }
+
+    public EmbeddedTestServer getTestServer() {
+        return mTestServerRule.getServer();
+    }
+
+    public String getTestDataURL(String path) {
+        return getTestServer().getURL("/weblayer/test/data/" + path);
+    }
+}
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index e42345a..136dccf 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -130,7 +130,10 @@
   testonly = true
   resources_package = "org.chromium.browserfragment.shell"
 
-  sources = [ "browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java" ]
+  sources = [
+    "browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java",
+    "browserfragment_shell_apk/src/org/chromium/browserfragment/shell/InstrumentationActivity.java",
+  ]
 
   deps = [
     ":browserfragment_service_provider_resources",
diff --git a/weblayer/shell/android/browserfragment_shell_apk/AndroidManifest.xml b/weblayer/shell/android/browserfragment_shell_apk/AndroidManifest.xml
index 6db87b6..6e14031 100644
--- a/weblayer/shell/android/browserfragment_shell_apk/AndroidManifest.xml
+++ b/weblayer/shell/android/browserfragment_shell_apk/AndroidManifest.xml
@@ -25,6 +25,12 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity android:name="InstrumentationActivity"
+                  android:launchMode="singleTask"
+                  android:theme="@style/ShellTheme"
+                  android:windowSoftInputMode="adjustResize"
+                  android:exported="true">
+        </activity>
 
         <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
 
diff --git a/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/InstrumentationActivity.java b/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/InstrumentationActivity.java
new file mode 100644
index 0000000..e74cb53
--- /dev/null
+++ b/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/InstrumentationActivity.java
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.browserfragment.shell;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.chromium.browserfragment.Browser;
+
+/**
+ * Activity for running instrumentation tests.
+ */
+public class InstrumentationActivity extends AppCompatActivity {
+    private ListenableFuture<Browser> mBrowserFuture;
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mBrowserFuture = Browser.create(getApplicationContext());
+    }
+
+    public ListenableFuture<Browser> getBrowserFuture() {
+        return mBrowserFuture;
+    }
+}
\ No newline at end of file