Commit 4f2ac337 authored by dullweber's avatar dullweber Committed by Commit Bot

Expose Add/RemoveActionCallback to java

Add support for testing user actions from Java by exposing
addActionCallback and removeActionCallback.
Add a UserActionTester class for java tests.
Add some tests for BrowsingDataBridge as an example.

There is already a similar approach for histogram tests using
getHistogramValueCountForTesting().

BUG=

Review-Url: https://codereview.chromium.org/2956283002
Cr-Commit-Position: refs/heads/master@{#485616}
parent 72fd03ef
......@@ -2731,6 +2731,7 @@ if (is_android) {
"test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java",
"test/android/javatests/src/org/chromium/base/test/util/TestThread.java",
"test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java",
"test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java",
"test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java",
"test/android/javatests/src/org/chromium/base/test/util/parameter/BaseParameter.java",
"test/android/javatests/src/org/chromium/base/test/util/parameter/Parameter.java",
......
......@@ -6,6 +6,7 @@ package org.chromium.base.metrics;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
/**
......@@ -49,5 +50,36 @@ public class RecordUserAction {
});
}
/**
* Interface to a class that receives a callback for each UserAction that is recorded.
*/
public interface UserActionCallback {
@CalledByNative("UserActionCallback")
void onActionRecorded(String action);
}
private static long sNativeActionCallback;
/**
* Register a callback that is executed for each recorded UserAction.
* Only one callback can be registered at a time.
* The callback has to be unregistered using removeActionCallbackForTesting().
*/
public static void setActionCallbackForTesting(UserActionCallback callback) {
assert sNativeActionCallback == 0;
sNativeActionCallback = nativeAddActionCallbackForTesting(callback);
}
/**
* Unregister the UserActionCallback.
*/
public static void removeActionCallbackForTesting() {
assert sNativeActionCallback != 0;
nativeRemoveActionCallbackForTesting(sNativeActionCallback);
sNativeActionCallback = 0;
}
private static native void nativeRecordUserAction(String action);
private static native long nativeAddActionCallbackForTesting(UserActionCallback callback);
private static native void nativeRemoveActionCallbackForTesting(long callbackId);
}
......@@ -5,9 +5,19 @@
#include "base/android/record_user_action.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/user_metrics.h"
#include "jni/RecordUserAction_jni.h"
namespace {
struct ActionCallbackWrapper {
base::ActionCallback action_callback;
};
} // namespace
namespace base {
namespace android {
......@@ -17,6 +27,34 @@ static void RecordUserAction(JNIEnv* env,
RecordComputedAction(ConvertJavaStringToUTF8(env, j_action));
}
static void OnActionRecorded(const JavaRef<jobject>& callback,
const std::string& action) {
JNIEnv* env = AttachCurrentThread();
Java_UserActionCallback_onActionRecorded(
env, callback, ConvertUTF8ToJavaString(env, action));
}
static jlong AddActionCallbackForTesting(
JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& callback) {
// Create a wrapper for the ActionCallback, so it can life on the heap until
// RemoveActionCallbackForTesting() is called.
auto* wrapper = new ActionCallbackWrapper{base::Bind(
&OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
base::AddActionCallback(wrapper->action_callback);
return reinterpret_cast<intptr_t>(wrapper);
}
static void RemoveActionCallbackForTesting(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
jlong callback_id) {
DCHECK(callback_id);
auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
base::RemoveActionCallback(wrapper->action_callback);
delete wrapper;
}
// Register native methods
bool RegisterRecordUserAction(JNIEnv* env) {
return RegisterNativesImpl(env);
......
// Copyright 2017 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.base.test.util;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordUserAction;
import java.util.ArrayList;
import java.util.List;
/**
* A util class that records UserActions.
*/
public class UserActionTester implements RecordUserAction.UserActionCallback {
private List<String> mActions;
public UserActionTester() {
mActions = new ArrayList<>();
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
RecordUserAction.setActionCallbackForTesting(UserActionTester.this);
}
});
}
public void tearDown() {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
RecordUserAction.removeActionCallbackForTesting();
}
});
}
@Override
public void onActionRecorded(String action) {
mActions.add(action);
}
public List<String> getActions() {
return mActions;
}
@Override
public String toString() {
return "Actions: " + mActions.toString();
}
}
......@@ -306,7 +306,7 @@ public class ClearBrowsingDataPreferences extends PreferenceFragment
}
/**
* Notify subclasses that browsing data is about to be cleared.
* Notifies subclasses that browsing data is about to be cleared.
*/
protected void onClearBrowsingData() {}
......
......@@ -1604,6 +1604,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoUtilsTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataBridgeTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasicTest.java",
"javatests/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManagerNativeTest.java",
......
// Copyright 2017 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.browser.preferences.privacy;
import static org.junit.Assert.assertThat;
import android.support.test.filters.SmallTest;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.UserActionTester;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
import org.chromium.chrome.browser.browsing_data.TimePeriod;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
/**
* Integration tests for ClearBrowsingDataPreferences.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
ChromeActivityTestRule.DISABLE_NETWORK_PREDICTION_FLAG})
public class BrowsingDataBridgeTest {
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
private CallbackHelper mCallbackHelper;
private BrowsingDataBridge.OnClearBrowsingDataListener mListener;
private UserActionTester mActionTester;
@Before
public void setUp() throws Exception {
mCallbackHelper = new CallbackHelper();
mListener = new BrowsingDataBridge.OnClearBrowsingDataListener() {
@Override
public void onBrowsingDataCleared() {
mCallbackHelper.notifyCalled();
}
};
mActivityTestRule.startMainActivityOnBlankPage();
mActionTester = new UserActionTester();
}
@After
public void tearDown() throws Exception {
mActionTester.tearDown();
}
/**
* Test no clear browsing data calls.
*/
@Test
@SmallTest
public void testNoCalls() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingData(
mListener, new int[] {}, TimePeriod.ALL_TIME);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.contains("ClearBrowsingData_Everything"));
}
/**
* Test cookies deletion.
*/
@Test
@SmallTest
public void testCookiesDeleted() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingData(
mListener, new int[] {BrowsingDataType.COOKIES}, TimePeriod.LAST_HOUR);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.containsInAnyOrder("ClearBrowsingData_LastHour",
"ClearBrowsingData_MaskContainsUnprotectedWeb",
"ClearBrowsingData_ChannelIDs", "ClearBrowsingData_Cookies"));
}
/**
* Test history deletion.
*/
@Test
@SmallTest
public void testHistoryDeleted() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingData(
mListener, new int[] {BrowsingDataType.HISTORY}, TimePeriod.LAST_DAY);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.containsInAnyOrder("ClearBrowsingData_LastDay",
"ClearBrowsingData_MaskContainsUnprotectedWeb",
"ClearBrowsingData_History"));
}
/**
* Test deleting cache and content settings.
*/
@Test
@SmallTest
public void testClearingSiteSettingsAndCache() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingData(mListener,
new int[] {
BrowsingDataType.CACHE, BrowsingDataType.SITE_SETTINGS,
},
TimePeriod.FOUR_WEEKS);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.containsInAnyOrder("ClearBrowsingData_LastMonth",
"ClearBrowsingData_MaskContainsUnprotectedWeb", "ClearBrowsingData_Cache",
"ClearBrowsingData_ShaderCache", "ClearBrowsingData_ContentSettings"));
}
/**
* Test deleting cache and content settings with important sites.
*/
@Test
@SmallTest
public void testClearingSiteSettingsAndCacheWithImportantSites() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingDataExcludingDomains(mListener,
new int[] {
BrowsingDataType.CACHE, BrowsingDataType.SITE_SETTINGS,
},
TimePeriod.FOUR_WEEKS, new String[] {"google.com"}, new int[] {1},
new String[0], new int[0]);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.containsInAnyOrder("ClearBrowsingData_LastMonth",
// ClearBrowsingData_MaskContainsUnprotectedWeb is logged
// twice because important storage is deleted separately.
"ClearBrowsingData_MaskContainsUnprotectedWeb",
"ClearBrowsingData_MaskContainsUnprotectedWeb", "ClearBrowsingData_Cache",
"ClearBrowsingData_ShaderCache", "ClearBrowsingData_ContentSettings"));
}
/**
* Test deleting all browsing data. (Except bookmarks, they are deleted in Java code)
*/
@Test
@SmallTest
public void testClearingAll() throws Exception {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BrowsingDataBridge.getInstance().clearBrowsingData(mListener,
new int[] {
BrowsingDataType.CACHE, BrowsingDataType.COOKIES,
BrowsingDataType.FORM_DATA, BrowsingDataType.HISTORY,
BrowsingDataType.PASSWORDS, BrowsingDataType.SITE_SETTINGS,
},
TimePeriod.LAST_WEEK);
}
});
mCallbackHelper.waitForCallback(0);
assertThat(mActionTester.toString(), mActionTester.getActions(),
Matchers.containsInAnyOrder("ClearBrowsingData_LastWeek",
"ClearBrowsingData_MaskContainsUnprotectedWeb", "ClearBrowsingData_Cache",
"ClearBrowsingData_ShaderCache", "ClearBrowsingData_Cookies",
"ClearBrowsingData_ChannelIDs", "ClearBrowsingData_Autofill",
"ClearBrowsingData_History", "ClearBrowsingData_Passwords",
"ClearBrowsingData_ContentSettings"));
}
}
\ No newline at end of file
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