Commit f4d9d461 authored by Christopher Grant's avatar Christopher Grant Committed by Commit Bot

Modules: Add native library coverage to bundle smoke test

- Add a new test case to exercise a native library.

- Generalize the display of the result dialog for multiple test cases.

- Specify a "pass" or "fail" in the results dialog text, to be parsed by
  the test, and also aid in manual testing (where the test activity may
  be spawned manually).

- Component build coverage is stubbed out; it will be added once the
  component build places feature libs in DFMs.

Bug: 989646
Change-Id: I724d0725f71ad7897ceb7b81d9fe7d22183889ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1796308
Commit-Queue: Christopher Grant <cjgrant@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#696035}
parent 3f1261c4
......@@ -1203,6 +1203,7 @@ chrome_common_shared_library("libchrome") {
if (enable_vr) {
deps += [ "//chrome/browser/android/vr:module_factory" ]
}
deps += [ "//chrome/android/features/test_dummy/internal:base_module_native" ]
allow_partitions = true
module_descs = chrome_modern_module_descs
......@@ -1219,6 +1220,7 @@ chrome_common_shared_library("libchromefortest") {
":chrome_jni_for_test_registration($default_toolchain)",
"//base/test:test_support",
"//chrome:chrome_android_core",
"//chrome/android/features/test_dummy/internal:base_module_native",
"//chrome/browser/android/metrics:ukm_utils_for_test",
"//components/autofill_assistant/browser:test_support",
"//components/crash/android:crash_android",
......@@ -1466,6 +1468,8 @@ template("libmonochrome_apk_or_bundle_tmpl") {
if (enable_vr) {
deps += [ "//chrome/browser/android/vr:module_factory" ]
}
deps +=
[ "//chrome/android/features/test_dummy/internal:base_module_native" ]
is_monochrome = true
allow_partitions = true
......
......@@ -2,10 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//build/config/android/rules.gni")
import("//chrome/android/modules/buildflags.gni")
android_library("java") {
deps = [
":base_module_java",
"//base:base_java",
"//chrome/android/features/test_dummy/public:java",
"//third_party/android_deps:android_support_v7_appcompat_java",
......@@ -15,7 +18,22 @@ android_library("java") {
[ "java/src/org/chromium/chrome/features/test_dummy/TestDummyImpl.java" ]
}
# Code that should go into the base module.
source_set("native") {
sources = [
"test_dummy.cc",
]
deps = [
"//base",
"//chrome/android/features/test_dummy/public:native",
]
}
# Java code that should go into the base module. If this were a normal feature,
# this target would reside in the client code using the module. Since this is a
# test dummy module, it has no pre-existing client, and hence the target is
# squatting here for convenience. The same is true for the corresponding native
# target.
android_library("base_module_java") {
deps = [
"//base:base_java",
......@@ -24,5 +42,33 @@ android_library("base_module_java") {
]
java_files = [
"java/src/org/chromium/chrome/features/test_dummy/TestDummyActivity.java",
"java/src/org/chromium/chrome/features/test_dummy/TestDummySupport.java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
deps += [ "//base:jni_java" ]
}
buildflag_header("buildflags") {
header = "buildflags.h"
flags = [ "USE_NATIVE_MODULES=$use_native_modules" ]
}
source_set("base_module_native") {
sources = [
"test_dummy_client.cc",
]
deps = [
":test_dummy_jni_headers",
"//base",
]
public_deps = [
":buildflags",
]
}
generate_jni("test_dummy_jni_headers") {
sources = [
"java/src/org/chromium/chrome/features/test_dummy/TestDummySupport.java",
]
}
......@@ -15,10 +15,11 @@ import java.util.Locale;
/** Test dummy implementation. */
public class TestDummyImpl implements TestDummy {
@IntDef({TestCase.EXECUTE_JAVA})
@IntDef({TestCase.EXECUTE_JAVA, TestCase.EXECUTE_NATIVE})
@Retention(RetentionPolicy.SOURCE)
private @interface TestCase {
int EXECUTE_JAVA = 0;
int EXECUTE_NATIVE = 1;
}
@Override
......@@ -29,20 +30,29 @@ public class TestDummyImpl implements TestDummy {
case TestCase.EXECUTE_JAVA:
executeJava(activity);
break;
case TestCase.EXECUTE_NATIVE:
executeNative(activity);
break;
default:
throw new RuntimeException("Unknown test case " + testCase);
}
}
private void executeJava(Activity activity) {
showDoneDialog(activity, TestCase.EXECUTE_JAVA);
}
private void showDoneDialog(Activity activity, @TestCase int testCase) {
private void showDoneDialog(Activity activity, @TestCase int testCase, boolean pass) {
String message = "Test Case %d: " + (pass ? "pass" : "fail");
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Test Dummy Result");
builder.setMessage(String.format(Locale.US, "Test Case %d: done", testCase));
builder.setMessage(String.format(Locale.US, message, testCase));
builder.setCancelable(true);
builder.create().show();
}
private void executeJava(Activity activity) {
showDoneDialog(activity, TestCase.EXECUTE_JAVA, true);
}
private void executeNative(Activity activity) {
boolean result = TestDummySupport.openAndVerifyNativeLibrary();
showDoneDialog(activity, TestCase.EXECUTE_NATIVE, result);
}
}
// Copyright 2019 The Chromium 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 org.chromium.chrome.features.test_dummy;
import org.chromium.base.annotations.NativeMethods;
/** Support class to proxy JNI calls from module Java to module native. */
//@MainDex
public class TestDummySupport {
/** JNI calls for exercising native parts of the DFM. */
@NativeMethods
public interface Natives {
boolean openAndVerifyNativeLibrary();
}
public static boolean openAndVerifyNativeLibrary() {
return TestDummySupportJni.get().openAndVerifyNativeLibrary();
}
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/android/features/test_dummy/public/test_dummy.h"
#include "base/logging.h"
namespace test_dummy {
int TestDummy() {
// Log something to utilize base library code. This is necessary to ensure
// that calls to base code are linked properly.
LOG(WARNING) << "Running test dummy native library.";
return 123;
}
} // namespace test_dummy
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <android/dlext.h>
#include <dlfcn.h>
#include "base/android/bundle_utils.h"
#include "base/logging.h"
#include "chrome/android/features/test_dummy/internal/buildflags.h"
#include "chrome/android/features/test_dummy/internal/test_dummy_jni_headers/TestDummySupport_jni.h"
static jboolean JNI_TestDummySupport_OpenAndVerifyNativeLibrary(JNIEnv* env) {
#if BUILDFLAG(USE_NATIVE_MODULES)
// Note that the library opened here is not closed. dlclosing() libraries has
// proven to be problematic. See https://crbug.com/994029.
void* handle =
base::android::BundleUtils::DlOpenModuleLibraryPartition("test_dummy");
if (handle == nullptr) {
LOG(ERROR) << "Cannot open test library: " << dlerror();
return false;
}
void* symbol = dlsym(handle, "TestDummyEntrypoint");
if (symbol == nullptr) {
LOG(ERROR) << "Cannot find test library symbol";
return false;
}
typedef int TestFunction();
TestFunction* test_function = reinterpret_cast<TestFunction*>(symbol);
if (test_function() != 123) {
LOG(ERROR) << "Unexpected value from test library";
return false;
}
#endif
return true;
}
......@@ -8,3 +8,9 @@ android_library("java") {
java_files =
[ "java/src/org/chromium/chrome/features/test_dummy/TestDummy.java" ]
}
source_set("native") {
sources = [
"test_dummy.h",
]
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_ANDROID_FEATURES_TEST_DUMMY_PUBLIC_TEST_DUMMY_H_
#define CHROME_ANDROID_FEATURES_TEST_DUMMY_PUBLIC_TEST_DUMMY_H_
namespace test_dummy {
// The test_dummy feature's token native entrypoint.
int TestDummy();
} // namespace test_dummy
#endif // CHROME_ANDROID_FEATURES_TEST_DUMMY_PUBLIC_TEST_DUMMY_H_
......@@ -21,8 +21,10 @@ import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
import org.chromium.chrome.test.pagecontroller.utils.UiLocatorHelper;
/** Smoke Test for Chrome bundles. */
@SmallTest
......@@ -46,18 +48,34 @@ public class ChromeBundleSmokeTest {
mChromeUiRule.launchIntoNewTabPageOnFirstRun();
}
@Test
public void testModuleInstall() {
// Send intent that makes Chrome install the test dummy module.
private void runTestActivity(int testCase) {
// This intent will trigger installation of the module if not present.
Context context = InstrumentationRegistry.getContext();
Intent intent = new Intent();
intent.setComponent(new ComponentName(mPackageName, TARGET_ACTIVITY));
intent.putExtra("test_case", 0); // Test case EXECUTE_JAVA.
intent.putExtra("test_case", testCase);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
// Wait for done dialog to show up.
Assert.assertTrue(UiAutomatorUtils.getInstance().getLocatorHelper().isOnScreen(
Ui2Locators.withText("Test Case 0: done")));
final String prefixText = "Test Case " + testCase + ": ";
IUi2Locator locator = Ui2Locators.withTextContaining(prefixText);
// Wait for result dialog to show up.
UiLocatorHelper locatorHelper = UiAutomatorUtils.getInstance().getLocatorHelper();
Assert.assertTrue(locatorHelper.isOnScreen(locator));
// Ensure the dialog text indicates a pass.
final String passText = prefixText + "pass";
Assert.assertEquals(locatorHelper.getOneTextImmediate(locator, null), passText);
}
@Test
public void testModuleJavaCodeExecution() {
runTestActivity(0); // Test case EXECUTE_JAVA.
}
@Test
public void testModuleNativeCodeExecution() {
runTestActivity(1); // Test case EXECUTE_NATIVE.
}
}
......@@ -13,3 +13,16 @@ android_library("java") {
"//chrome/android/modules/test_dummy/public:java",
]
}
source_set("native") {
sources = [
"entrypoints.cc",
]
deps = [
"//chrome/android/features/test_dummy/public:native",
]
# Test dummy native entrypoints belong in the partition.
cflags = [ "-fsymbol-partition=libtest_dummy.so" ]
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/android/features/test_dummy/public/test_dummy.h"
extern "C" {
__attribute__((visibility("default"))) int TestDummyEntrypoint() {
return test_dummy::TestDummy();
}
} // extern "C"
......@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//chrome/android/modules/buildflags.gni")
test_dummy_module_desc = {
name = "test_dummy"
android_manifest =
......@@ -10,4 +12,13 @@ test_dummy_module_desc = {
"//chrome/android/features/test_dummy/internal:java",
"//chrome/android/modules/test_dummy/internal:java",
]
if (use_native_modules) {
native_deps = [
"//chrome/android/features/test_dummy/internal:native",
"//chrome/android/modules/test_dummy/internal:native",
]
native_entrypoints =
"//chrome/android/modules/test_dummy/internal/entrypoints.lst"
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment